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

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

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

##◆イベント・リスナのクラス作成(実装)
###今回の機能：アクセス時に、アクセスIPをLogに記述する

```:コマンド
php artisan event:generate
```

このコマンドで、イベントクラスとリスナークラスを作ってくれるが、そのために、
<b>EventServiceProvider</b>にイベントとリスナーを登録する必要がある。
(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

<b>トレイト</b>
・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
```

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

<b>redis connection refused</b>
のエラーが出た。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