- 投稿日:2019-11-27T20:44:07+09:00
[Laravel]テキスト中のURLをリンクに自動で置換する
概要
テキスト中にあるURLを自動でリンクに置換することでフロント側でリンク化する手間が省けるのでバックエンドで処理をしてみました。
URLをリンク化する
今回はテキスト中にURLの行のみのがある場合にリンク化させます。文章中のURLは、リンク化しない正規表現にしました。
https://test //リンク化 https://test テスト //無視$textのURLをリンク化
public static function replaceUrl($text) { $texts = explode(PHP_EOL, $text); //PHP_EOLは,改行コードをあらわす.改行があれば分割する $pattern = '/^https?:\/\/[^\s \\\|`^"\'(){}<>\[\]]*$/'; //正規表現パターン $replacedTexts = array(); //空の配列を用意 foreach ($texts as $value) { $replace = preg_replace_callback($pattern, function ($matches) { //textが1行ごとに正規表現にmatchするか確認する if (isset($matches[1])) { return $matches[0]; //$matches[0] がマッチした全体を表す } //既にリンク化してあれば置換は必要ないので、配列に代入 return '<a href="' . $matches[0] . '" target="_blank" rel="noopener">' . $matches[0] . '</a>'; }, $value); $replacedTexts[] = $replace; //リンク化したコードを配列に代入 } return implode(PHP_EOL, $replacedTexts); //配列にしたtextを文字列にする }使用したメソッド
explode — 文字列を文字列により分割する
(https://www.php.net/manual/ja/function.explode.php)
preg_replace_callback — 正規表現検索を行い、コールバック関数を使用して置換を行う
(https://www.php.net/manual/ja/function.preg-replace-callback.php)
implode — 配列要素を文字列により連結する
(https://www.php.net/manual/ja/function.implode.php)使っている正規表現について
$pattern = '/^https?:\/\/[^\s \\\|`^"\'(){}<>\[\]]*$/'
^
先頭にhttpsまたはhttpにマッチする
\
直後の正規表現記号を エスケープ
[^...]
角括弧に含まれる文字以外にマッチ
ここでは、半角、全角スペースとURLで除外される記号以外とマッチさせています
*
直前の文字が 0回以上 繰り返す場合にマッチします。最長一致。
?
直前の文字が 0個か1個 の場合にマッチします。最長一致。あとがき
文字列を改行ごとに分割して、分割したものが正規表現にマッチした場合URLをリンク化し、
それをまた文字列に変換したりとするなど色々考えたので勉強になりました。
正規表現もURLとマッチするものを考える作業が楽しかったです。参考記事
- 投稿日:2019-11-27T20:44:07+09:00
[Laravel]テキスト中のURLをリンクに置換
概要
テキスト中にあるURLを自動でリンクに置換することでフロント側でリンク化する手間が省けるのでバックエンドで処理をしてみました。
URLをリンク化する
今回はURLの行のみの場合にリンク化させます。なので文章中のURLは、リンク化しない正規表現にしました。
https://test //リンク化 https://test テスト //無視$textのURLをリンク化
public static function replaceUrl($text) { $texts = explode(PHP_EOL, $text); //PHP_EOLは,改行コードをあらわす.改行があれば分割する $pattern = '/^https?:\/\/[^\s \\\|`^"\'(){}<>\[\]]*$/'; //正規表現パターン $replacedTexts = array(); //空の配列を用意 foreach ($texts as $value) { $replace = preg_replace_callback($pattern, function ($matches) { //textが1行ごとに正規表現にmatchするか確認する if (isset($matches[1])) { return $matches[0]; //$matches[0] がマッチした全体を表す } //既にリンク化してあれば置換は必要ないので、配列に代入 return '<a href="' . $matches[0] . '" target="_blank" rel="noopener">' . $matches[0] . '</a>'; }, $value); $replacedTexts[] = $replace; //リンク化したコードを配列に代入 } return implode(PHP_EOL, $replacedTexts); //配列にしたtextを文字列にする }使用したメソッド
explode — 文字列を文字列により分割する
(https://www.php.net/manual/ja/function.explode.php)
preg_replace_callback — 正規表現検索を行い、コールバック関数を使用して置換を行う
(https://www.php.net/manual/ja/function.preg-replace-callback.php)
implode — 配列要素を文字列により連結する
(https://www.php.net/manual/ja/function.implode.php)使っている正規表現について
$pattern = '/^https?:\/\/[^\s \\\|`^"\'(){}<>\[\]]*$/'
^
先頭にhttpsまたはhttpにマッチする
\
直後の正規表現記号を エスケープ
[^...]
角括弧に含まれる文字以外にマッチ
ここでは、半角、全角スペースとURLで除外される記号以外とマッチさせています
*
直前の文字が 0回以上 繰り返す場合にマッチします。最長一致。
?
直前の文字が 0個か1個 の場合にマッチします。最長一致。あとがき
文字列を改行ごとに分割して、分割したものが正規表現にマッチした場合URLをリンク化し、
それをまた文字列に変換したりとするなど色々考えたので勉強になりました。
正規表現もURLとマッチするものを考える作業が楽しかったです。参考記事
- 投稿日:2019-11-27T20:12:16+09:00
Laravel でむやみに Model all() を使うとパフォーマンスが悪くなる
はじめに
(サンプルのバージョンは 5.5 で書いてます)
Laravelって便利。リレーションだって簡単に貼れます。
しかし、書き方によって予期しない挙動したので備忘録として残します。実際にやってみた
まずサンプルとして、ユーザ情報を扱うテーブルと、トレーニング情報を扱うテーブルを用意。
CREATE TABLE `users` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID', `name` varchar(64) NOT NULL COMMENT '名前', `deleted_at` datetime DEFAULT NULL COMMENT '削除日時', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT='ユーザ'; CREATE TABLE `trainings` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID', `trained_user_id` int(10) unsigned NOT NULL COMMENT 'トレーニング者', `summary` varchar(255) NOT NULL COMMENT '概要', `deleted_at` datetime DEFAULT NULL COMMENT '削除日時', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT='トレーニング';トレーニング情報と一緒に、トレーニング者のユーザ名を取得したいとき、
Modelクラスでこのように関数を設置し、belongsToでリレーションを貼っておくと、
Training情報を取得した時にも trained_user関数 を呼び出しただけで users 情報を取得できます。Training.phpuse Illuminate\Database\Eloquent\Model; /** * Class Training */ class Training extends Model { public function trained_user() { return $this->belongsTo(User::class); } }$trainings = Training::all(); $trainings[0]->trained_user->name; // トレーニング者の名前が取れるでも、やったね!と思ってクエリログを見ると愕然します...
select * from `trainings` where `trainings`.`deleted_at` is null select * from `users` where `users`.`id` = '1' and `users`.`deleted_at` is null limit 1 select * from `users` where `users`.`id` = '2' and `users`.`deleted_at` is null limit 1 select * from `users` where `users`.`id` = '3' and `users`.`deleted_at` is null limit 1みてください、・・・ユーザ分毎回SQL投げてる;;;;
めっちゃパフォーマンス悪い;;;;反省して書き直し。
$trainings = Training::with(['trained_user'])->get(); // trained_user を設定する $trainings[0]->trained_user->name; // 同様にトレーニング者の名前が取れるクエリログは・・・
select * from `trainings` where `trainings`.`deleted_at` is null select * from `users` where `users`.`id` in ('1', '2', '3') and `users`.`deleted_at` is null期待どおりの結果になりました!
というわけで、リレーション貼ってる時は気をつけましょう〜
- 投稿日:2019-11-27T19:38:19+09:00
Laravel 5.8でオリジナルのイベントを定義してみた話
はじめに
こちらの記事は、社内knowledgeからの移行記事になります。
Laravelで自作イベントを定義するまでの一連の流れ。
【参考にした公式】
【参考記事】
- Laravel5.7でEventを利用してみる - qiita【ソース】
- GitHub環境
- Laravel 5.8.23
本編
1.イベントとリスナをサービスプロバイダに定義
application/app/Providers/EventServiceProvider.phpprotected $listen = [ 'App\Events\TaxIncreased' => [ 'App\Listeners\TaxIncreasedNotification' ], ];2.Artisanコマンドでイベントとリスナを作成
# php artisan event:generate Events and listeners generated successfully!
3.リスナにイベント処理を定義
今回は発生したイベントのオブジェクトを返すだけ。
application/app/Listeners/TaxIncreasedNotification.phppublic function handle(TaxIncreased $event) { return $event; }4.イベントを定義
application/app/Events/TaxIncreased.phppublic $name; public $price; public $taxRate; public $taxIncludingPrice; /** * TaxIncreased constructor. * * @param string $name * @param int $price * @param bool $isReduceTax */ public function __construct(string $name, int $price, bool $isReduceTax){ $this->name = $name; $this->price = $price; $this->taxRate = ($isReduceTax) ? 0.08 : 0.10; // 税込み価格計算 $this->taxIncludingPrice = (int) $this->price * ( 1 + $this->taxRate); }5.イベントを呼び出す
今回はコントローラのアクションからイベントを呼び出す。
application/app/Http/Controllers/HomeController.phppublic function index() { $eatIn = event(new TaxIncreased('タピオカ',1000,false))[0]; $takeOut = event(new TaxIncreased('タピオカ',1000,true ))[0]; return view('home',compact(['eatIn','takeOut'])); }結果
軽減税率イベントが発火した。
- 投稿日:2019-11-27T19:30:35+09:00
Laravel5.8でSlackに通知(Notifications)を送ってみた話
はじめに
こちらの記事は、社内knowledgeからの移行記事になります。
初めての技術投稿で探り探りな面があります。
Laravelに搭載されている通知(Notifications)機能を使って、
ログインしたときにSlackに通知を送る機能を作成してみました。不正ログイン通知や購入処理完了時のメッセージ送信などに使えそうですね。
【参考にした公式】
- 公式(英語)
- ReaDoubleなお、ログインイベントのリスナー設定は省略します。
GitHubにコードはあります。環境
- Vagrant(Homestead)
- Laravel 5.8.23
Slackの設定
- 送信したいワークスペースの管理画面(https://[ワークスペース名].slack.com/apps) から、投稿したいチャンネルを選択してIncoming Webhookを追加する。
- 追加後の画面からWebhook URLを回収する。 ***
パッケージのインストール
composer require guzzlehttp/guzzle
composer require laravel/slack-notification-channel
通知クラスの作成
下記のコマンドでSlackという通知クラスが作成されます。
viaメソッドで複数定義するとどうなるかは試してみる。
php artisan make:notification Slack
通知クラス(App\Notifications\Slack)
Notification.php<?php namespace App\Notifications; use Illuminate\Bus\Queueable; use Illuminate\Notifications\Notification; use Illuminate\Notifications\Messages\SlackMessage; class Slack extends Notification { use Queueable; protected $content; protected $channel; protected $name; /** * Create a new notification instance. * @param string $message * @return void */ public function __construct(string $message) { $this->channel = env('SLACK_CHANNEL'); $this->name = env('SLACK_NAME'); $this->content = $message; } /** * Get the notification's delivery channels. * * @param mixed $notifiable * @return array */ public function via($notifiable) { return ['slack']; } /** * Get the mail representation of the notification. * * @param mixed $notifiable * @return SlackMessage */ public function toSlack($notifiable) { return (new SlackMessage) ->from($this->name, ':ghost:') ->to($this->channel) ->content($this->content); } }ログインイベントのリスナーを作成
ログイン時に通知を送信するためのクラスを作成する。
ログイン イベントリスナークラス(App\Listeners\LoginListener)
<?php namespace App\Listeners; use Illuminate\Auth\Events\Login; use Illuminate\Http\Request; use App\Notifications\Slack; use App\Notification\Slack\SlackRepository; use Illuminate\Support\Facades\Notification as FacNotification; class LoginListener { protected $slackRepository; /** * Create the event listener. * * @param Request $request * @param SlackRepository $slackRepository */ public function __construct(Request $request, SlackRepository $slackRepository) { $this->request = $request; $this->slackRepository = $slackRepository; } /** * Handle the event. * * @param Login $event * @return void */ public function handle(Login $event) { //ログイン成功時の処理 // リポジトリで通知を送信します。 $this->slackRepository->notify(new Slack("リポジトリ:{$event->user->name}")); // ファサードで通知を送信します。 FacNotification::route('slack', env('SLACK_WEBHOOK_URL')) ->notify(new Slack("ファサード:{$event->user->name}")); // モデルで通知を送信します。 $event->user->notify(new Slack("モデル:{$event->user->name}")); } }
ログインすると...
絶対通知送信するマンに煽られる。
まとめ
- Laravelで通知機能を実装
- クラスから通知を出す場合
- 通知を送りたいクラスでNotifiableをtrait
- artisanコマンドで通知クラスを作る
- 通知クラスにviaメソッド,to〇〇メソッドを実装
- 通知を送りたいクラスにrouteNotificationFor〇〇メソッドを実装
- Notification ファサードを使う場合
use Illuminate\Support\Facades\Notification; Notification::route($channel,$send) ->notify($clsNotification));参考
その他の参考はこちら。
- 投稿日:2019-11-27T18:24:19+09:00
【Laravel】Carbonで今が上期下期を判断する
Laravelを用いてますが、
Carbon
はcomposer
から入れれるので同じです。上期は基本的に4月から9月末、下期は10月から3月末のようなので、そのように定義します。
今回は
web.php
にコールバックに書いていきますweb.phpRoute::get('/',function(){ $start = today()->month(4)->firstOfMonth(); $end = today()->month(9)->endOfMonth(); });上期の開始と終了を定義しました。
today()
はLaravelに用意されているヘルパです。
new Carbon('today')
と同等になります。web.phpRoute::get('/',function(){ $start = today()->month(4)->firstOfMonth(); $end = today()->month(9)->endOfMonth(); echo today()->between($start , $end ) ? '上' : '下'); });あとは、今日が上期の範囲内か判断しているだけです。
- 投稿日:2019-11-27T16:01:49+09:00
【Laravel】データベースを作成しよう
こんにちは!
今回はデータベースにデータを保存できるようにすることを最終地点として説明します
大まかな流れは以下の通りです
1. 環境変数を設定する
2. データベースを作成する
3. データベースに接続する
4. データベースにデータを保存するでは、始めます!!
環境変数の編集
環境変数を編集していきます
環境変数は使用するOS上でのルールを決めている変数というイメージです。
ここで編集するファイルは二つです.envファイルの編集
.envファイルを編集します
下記のように編集していきましょう
データベースの名前はご自身のアプリケーション名に対応させてください
DB_USERNAME
はroot
、DB_PASSWORD
はsecret
で設定しましょう.env(一部) 編集前DB_DATABASE=laravel DB_USERNAME=root DB_PASSWORD=.env(一部) 編集後DB_DATABASE=laravel-project DB_USERNAME=root DB_PASSWORD=secretdatabase.phpの編集
.env
に合わしてdatabase.php
も編集しましょうconfig/database.php'mysql' => [ // : // : 'database' => env('DB_DATABASE', 'laravel-project'), 'username' => env('DB_USERNAME', 'root'), 'password' => env('DB_PASSWORD', 'secret'), // : // : ],データベース作成
MySQLにログインして、データベースを作成しましょう!
MySQLにrootユーザーでログインします。$ mysql -u rootMySQLにログイン完了後、
laravel-project
というデータベースを作成しますmysql> CREATE DATABASE `laravel-project`;下記のコマンドでデータベースが作成されているか確認しましょう
mysql> show databases;データベース接続
上で作成したデータベースに接続しましょう
ターミナル$ php artisan tinker Psy Shell v0.9.9 (PHP 7.3.9-1+ubuntu18.04.1+deb.sury.org+1 — cli) by Justin Hileman >>> DB::connection(); => Illuminate\Database\MySqlConnection {#2891}テーブル作成
マイグレーションして、テーブルを作成します。
マイグレーションする前に少し準備をします。文字列の最大文字数を191文字にする
マイグレーション時にエラーになるので設定しましょう
app/Providers/AppServiceProvider.phppublic function boot() { \Schema::defaultStringLength(191); }では、下記のコマンドでマイグレーションを行い、テーブルを作成しましょう
ターミナル$ php artisan migrateテーブルの作成が完了しました。
データ作成
一度に複数のデータを作成しましょう!!
まず、下記のコマンドを実行するとUsersTableSeeder.phpが生成されます。ターミナル$ php artisan make:seeder UsersTableSeeder生成されたUsersTableSeeder.phpのRun関数内に下記のように追記しましょう
database/seeds/UsersTableSeeder.phppublic function run(){ DB::table('users')->insert([ 'name' => 'test1', 'email' => 'test1@test.com', 'password' => bcrypt('test1') ]); }次に、DatabaseSeeder.phpのRun関数内をコメントアウトしてください
database/seeds/DatabaseSeeder.phppublic function run() { $this->call(UsersTableSeeder::class); }上記で編集したファイルを元にデータを作成しましょう
$ php artisan db:seed --class=UsersTableSeederコンソールから作成されたデータを確認しましょう
$ php artisan tinker >>> use App\User >>> User::all()上記のコマンドでテーブル内のデータが表示されます
表示されれば完了です!!疑問、気になるところがございましたら、質問、コメントよろしくお願いします!!!
- 投稿日:2019-11-27T11:52:13+09:00
Laravelにおける複数フィールドの最低限必須のバリデーション
TL;DR
required_without_allルールを使いましょう。
required_without_allルールについてと挙動の確認
公式ドキュメントを丹念に探せば見つかるのですが、わかりづらいバリデーションとして、
複数あるフィールドの中で、最低限ひとつは入力してもらいたい場合をチェックするバリデーションがあると思います。
例えば上のキャプチャのような、いくつかのSNSアカウントを入力するフィールドがあって、その中のどれかひとつでも入力してくれればオーケーみたいな場合です。
これはrequired_without_allルールでバリデーションできます。https://laravel.com/docs/6.x/validation#rule-required-without-all
https://readouble.com/laravel/6.x/ja/validation.html#rule-required-without-all指定した他のフィールドがすべて存在していない場合、このフィールドが存在し、かつ空でないことをバリデートします。
よく読めばこれが求めていたルールです! と納得するのですが、流し見程度だとちょっとわかりづらい。。
そこで今回はフォームリクエストのサンプルを作り、それをテストしてrequired_without_allルールの動きを確認します。
尚、Laravelのバージョンは6です。$ php artisan make:request SampleFormRequest Request created successfully. $ php artisan make:test --unit SampleFormRequestTest Test created successfully.<?php namespace App\Http\Requests; use Illuminate\Foundation\Http\FormRequest; class SampleFormRequest extends FormRequest { /** * Determine if the user is authorized to make this request. * * @return bool */ public function authorize() { return true; } /** * Get the validation rules that apply to the request. * * @return array */ public function rules() { return [ 'qiita' => 'required_without_all:github,twitter,instagram', 'github' => '', 'twitter' => '', 'instagram' => '', ]; } public function messages() { return [ 'qiita.required_without_all' => 'Qiita / GitHub / Twitter / Instagramの中で1個以上の項目を必ず入力してください。', ]; } }required_without_allのパラメータで他の項目を羅列します。
<?php namespace Tests\Unit; use Tests\TestCase; use Illuminate\Support\Facades\Validator; use App\Http\Requests\SampleFormRequest; class SampleFormRequestTest extends TestCase { /** * required_without_allルールのフォームリクエストのテスト * * @dataProvider validationDataProvider * * @param array $inputs * @param array $expected * @return void */ public function testValidation(array $inputs, array $expected) { $request = new SampleFormRequest(); $validator = Validator::make($inputs, $request->rules(), $request->messages()); $result = $validator->passes(); $errors = $validator->errors(); $this->assertEquals($expected['response'], $result); $this->assertEquals($expected['errors'], $errors->toArray()); } public function validationDataProvider() { return [ 'エラー' => [ [ 'qiita' => '', 'github' => '', 'twitter' => '', 'instagram' => '', ], [ 'response' => false, 'errors' => [ 'qiita' => ['Qiita / GitHub / Twitter / Instagramの中で1個以上の項目を必ず入力してください。'], ] ] ], '正常1' => [ [ 'qiita' => 'kiyc', 'github' => '', 'twitter' => '', 'instagram' => '', ], [ 'response' => true, 'errors' => [] ] ], '正常2' => [ [ 'qiita' => '', 'github' => 'kiyc', 'twitter' => '', 'instagram' => '', ], [ 'response' => true, 'errors' => [] ] ], ]; } }$ vendor/bin/phpunit tests/Unit/SampleFormRequestTest.php PHPUnit 8.4.1 by Sebastian Bergmann and contributors. ... 3 / 3 (100%) Time: 396 ms, Memory: 16.00 MB OK (3 tests, 6 assertions)すべて未入力だとエラーになり、ルールを指定したフィールドに入力がある場合、またはルールを指定していないフィールドに入力がない場合でもエラーにならないことを確認できました
このルールを見つけられなかったばかりに、私はわざわざカスタムルールを作ってしまったので、皆さん公式ドキュメントはしっかり読みましょう