◆メリット
イベントとリスナーを利用(オブザーバーパターン)すれば、コントローラーの一つのメソッドに書くコード量を減らすことができ、可読性・管理のしやすさが上がる。
◆オブザーバーパターンとは
監視する側(リスナー)と監視される側(イベント)に分けて、イベントの変化をリスナーが受け取って処理するという感じ
◆イベント・リスナのクラス作成(実装)
今回の機能:アクセス時に、アクセスIPをLogに記述する
php artisan event:generate
このコマンドで、イベントクラスとリスナークラスを作ってくれるが、そのために、
EventServiceProviderにイベントとリスナーを登録する必要がある。
(php artisan make:eventとphp artisan make:listenerで個別にも作れる)
(登録方法はほかにも、ファサードもしくはサービスコンテナのeventに登録する方法もある)
https://readouble.com/laravel/5.7/ja/events.html
protected $listen = [
//デフォルトで登録されているもの
'App\Events\Event' => [
'App\Listeners\EventListener',
],
//アクセスを検出するイベント
'App\Events\AccessDetectionEvent' => [
// アクセスIP記録リスナー
'App\Listeners\AccessIpRecordListener',
],
];
イベントの中にリスナーを記述するようにする(複数のイベント・リスナーに紐づけることも可能)。完了したら、
php artisan event:generate
で作成!
◆イベントクラスの実装
class AccessDetectionEvent
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $ip;
public function __construct($ip)
{
$this->ip = $ip;
}
public function broadcastOn()
{
return new PrivateChannel('channel-name');
}
}
今回は、イベントクラスはipを設置するだけ
二つめのメソッドのbroadcastOnはブロードキャストイベントを使いたい時用
[ブロードキャストイベント]https://blog.ytake.jp.net/entry/2015/12/15/023631
トレイト
・Dispatchable:イベントを発火させる(ディスパッチ)ときに利用
・InteractsWithSockets:soket.ioを使用したイベント通知時利用
・SerializesModels:キューと組み合わせて非同期イベントを発火させるためのもの
◆リスナークラスの実装
class AccessIpRecordListener
{
public function __construct()
{
//
}
public function handle(AccessDetectionEvent $event)
{
Log::info($event->ip);
}
}
イベントのipプロパティにアクセスして、その情報をLogに記述している。
◆コントローラーの実装
class SampleController extends Controller
{
public function sample(Request $request)
{
event(new AccessDetectionEvent($request->ip()));
dd('Logファイルみよう。');
}
}
eventメソッドでイベントを発行(ディスパッチ)している。
◆結果
[2019-04-06 00:00:00] local.INFO: 192.168.99.1
◆まとめ
コントローラー内のメソッドの処理の分割に成功した。
このイベント機能は、他にもキューを使った遅延処理や非同期で行う方法などがあるらしいから、やりたいと思う。
[参考にさせていただいた記事]
https://www.ritolab.com/entry/35
おまけ
◆非同期リスナーを実装
php artisan make:listener AccessIpRecordQueueListener
class AccessIpRecordQueueListener implements ShouldQueue
{
use InteractsWithQueue;
public function __construct()
{
//
}
public function handle($event)
{
Log::info($event->ip);
}
}
ShouldQueueを実装して、use InteractsWithQueue;を記述するだけでOK
あとは、同じようにLogに書き込む処理を追加する。
protected $listen = [
Registered::class => [
SendEmailVerificationNotification::class,
],
//アクセスを検出するイベント
AccessDetectionEvent::class => [
// アクセスIP記録リスナー
AccessIpRecordListener::class,
//アクセスIP記録リスナー(非同期)
AccessIpRecordQueueListener::class
],
];
非同期用のリスナーを登録する。
QUEUE_CONNECTION=sync
envファイルをみると、同期になっているので、非同期設定にする。
(データベース・redis・SQSなどを使える)
今回は、redisを使ってみる。
QUEUE_CONNECTION=redis
REDIS_HOST=redis
php artisan config:cache
設定を変えたので、設定のキャッシュをクリア
composer require predis/predis
Redisを使うために導入する。
php artisan queue:work
キューを監視させる(非同期リスナーの起動)
アクセスする
redis connection refused
のエラーが出た。dockerにredisを加えるのを忘れてた。
redis:
image: redis
container_name: redis
ports:
- "6379:6379"
docker-compose up -d
再びアクセス
root@○○:/var/www# php artisan queue:work
[2019-04-06 00:00:00][○○] Processing: App\Listeners\AccessIpRecordQueueListener
[2019-04-06 00:00:00][○○] Processed: App\Listeners\AccessIpRecordQueueListener
[2019-04-06 00:00:00] local.INFO: 192.168.99.1
[2019-04-06 00:00:00] local.INFO: 192.168.99.1
ちゃんと同期と非同期で2個Logが記述されていた。クリア
[参考にさせていただいた記事]
https://qiita.com/yk-m/items/d65dedfa291d9cbfed38