LoginSignup
13
17

More than 3 years have passed since last update.

Laravelのイベントを使ってみた

Last updated at Posted at 2019-04-06

◆メリット

イベントとリスナーを利用(オブザーバーパターン)すれば、コントローラーの一つのメソッドに書くコード量を減らすことができ、可読性・管理のしやすさが上がる。

◆オブザーバーパターンとは

監視する側(リスナー)と監視される側(イベント)に分けて、イベントの変化をリスナーが受け取って処理するという感じ

◆イベント・リスナのクラス作成(実装)

今回の機能:アクセス時に、アクセス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

EventServiceProvider
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メソッドでイベントを発行(ディスパッチ)している。

◆結果

Log
[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に書き込む処理を追加する。

EventServiceProvider
protected $listen = [
    Registered::class => [
        SendEmailVerificationNotification::class,
    ],
    //アクセスを検出するイベント
    AccessDetectionEvent::class => [
        // アクセスIP記録リスナー
        AccessIpRecordListener::class,
        //アクセスIP記録リスナー(非同期)
        AccessIpRecordQueueListener::class
    ],
];

非同期用のリスナーを登録する。

env
QUEUE_CONNECTION=sync

envファイルをみると、同期になっているので、非同期設定にする。
(データベース・redis・SQSなどを使える)
今回は、redisを使ってみる。

env
QUEUE_CONNECTION=redis
REDIS_HOST=redis
php artisan config:cache

設定を変えたので、設定のキャッシュをクリア

composer require predis/predis

Redisを使うために導入する。

php artisan queue:work

キューを監視させる(非同期リスナーの起動)
アクセスする

redis connection refused
のエラーが出た。dockerにredisを加えるのを忘れてた。

docker-compose.yml
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
Log
[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

13
17
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
17