Laravel5でのイベント処理と優先順位

# LaravelのEvent
Laravel5でのイベント発火と、それに応じるリスナーを登録するために使います。
リスナーの実行をキューに貯める事も可能です。
簡単な使い方と登録されたリスナーの実行順についてまとめてみました。

## Eventの使い方

### イベントの作成
まずは発火するために必要なイベントクラスを作ります。

今回は「ユーザーを登録したよ」ってイベントを作ってみます。
artisan make:event コマンドを使用すると楽ちんです。

```
$ cd /www/laravel5.app/htdocs/
$ php artisan make:event UserWasRegistered

$ cat app/Events/UserWasRegistered.php
<?php namespace App\Events;

use App\Events\Event;

use Illuminate\Queue\SerializesModels;

class UserWasRegistered extends Event {

        use SerializesModels;

        /**
         * Create a new event instance.
         *
         * @return void
         */
        public function __construct()
        {
                //
        }

}
```

app/Eventsディレクトリの下にイベントクラスが出来ましたね。
このクラスの名前は**「イベント名」**としても使用します。

コンストラクタではイベント発火時に渡されたパラメータを受け取る事ができます。
今回の例ではこのまま何もしないで使いますが、実際には登録されたユーザーの情報を受取るようにすると良いでしょう。

### リスナーの作成
続いてイベントの発火に応じて実行するリスナークラスを作ります。

それでは登録されたユーザーに対して確認メールを送信するクラスを作りましょう。こういう処理をイベントで行うと幸せになれます。
artisan handler:event コマンドを使うと楽ちんです。
（--eventオプションでイベント名を指定します）

```
$ php artisan handler:event EmailPurchaseConfirmation --event=UserWasRegistered

$ cat app/Handlers/Events/EmailPurchaseConfirmation.php
<?php namespace App\Handlers\Events;

use App\Events\UserWasRegistered;

use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldBeQueued;

class EmailPurchaseConfirmation {

        /**
         * Create the event handler.
         *
         * @return void
         */
        public function __construct()
        {
                //
        }

        /**
         * Handle the event.
         *
         * @param  UserWasRegistered  $event
         * @return void
         */
        public function handle(UserWasRegistered $event)
        {
                //
        }

}
```

app/Handlers/Eventsディレクトリの下にイベントクラスが出来ましたね。

handlerメソッドには先ほど作成したイベントクラスのインスタンスが渡されるので、
そいつが持ってるユーザ情報を使ってメール送信してあげたりしますが、今回は省略します。

### リスナーの登録
それでは作成したイベントクラスとリスナークラスを関連付けるためにイベントサービスプロバイダへ登録しましょう。

デフォルトだとこうなっているかと思います。

```
$ cat app\Providers\EventServiceProvider.php
<?php namespace App\Providers;

use Illuminate\Contracts\Events\Dispatcher as DispatcherContract;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;

class EventServiceProvider extends ServiceProvider {

        /**
         * The event handler mappings for the application.
         *
         * @var array
         */
        protected $listen = [
                'event.name' => [
                        'EventListener',
                ],
        ];

        /**
         * Register any other events for your application.
         *
         * @param  \Illuminate\Contracts\Events\Dispatcher  $events
         * @return void
         */
        public function boot(DispatcherContract $events)
        {
                parent::boot($events);

                //
        }

}
```

このlistenプロパティへ下記のように設定してあげます。（デフォルト値は消して構いません。）

```
    protected $listen = [
        'App\Events\UserWasRegistered' => [
            'App\Handlers\Events\EmailPurchaseConfirmation@handle',
        ],
    ];
```

keyはイベント名、valueは配列でリスナーを指定します。
リスナーは クラス名@メソッド名 のように書けます。

### イベント発火
さぁこれで準備は整いました。あとは意図したタイミングでイベントを発火させるだけです。
コントローラーではこんな感じで書く事ができます。

```
$response = event(new \App\Events\UserWasRegistered($user));
```

これでイベントが発火され、それに応じてリスナーが実行されます。

他にも様々なイベントの発火方法、リスナーの登録・実行方法があるので
詳しくは[ドキュメント](http://laravel.com/docs/5.0/events)を見たり、[Illuminate/Events/Dispatcher](https://github.com/illuminate/events/blob/master/Dispatcher.php)を覗いてみると良いですが
一部を簡単に紹介しつつ優先順位について説明したいと思います。

## ファサードを利用する
上記の例ではイベントの発火にヘルパーとして提供されているeventメソッドを使いました。
代わりにイベントファサードを利用することで、**どこからでもEventを発火することが出来ます。**

利用する場合は use Illuminate\Support\Facades\Event; したうえで
このように書きます。

```
$response = Event::fire(new \App\Events\UserWasRegistered($user));
```

## Event::Listen
リスナーはわざわざクラス定義しなくてもクロージャーを登録する事が出来ます。
たとえばこんな感じ

```
Event::listen('App\Events\UserWasRegistered', function($event)
{
    // イベントに応じた処理
}, 0);
```

**第3引数はpriority**です。指定しないとデフォルトで0になりますが、あえて書いてみました。

リスナークラスを定義した場合と違って重み付けを指定することができます。
**数字が大きいほど先に実行されます。**
priorityが同じリスナーは登録順に実行される事になります。

## ワイルドカードイベントリスナー
イベント名には*（アスタリスク）によるワイルドカードが利用可能です。
これにより複数のイベントをまとめてlistenする事が出来ます。

```
Event::listen('App\Events\*', function($event)
{
    // イベントに応じた処理
});
```

**ただし、ワイルドカードを利用するとpriorityの指定が無視されます。**


# 優先順位のまとめ
下記の1、2、3の順でリスナーが実行されます。

1. **Event::Listenにより登録されたリスナーでpriorityが高い順**
2. **サービスプロバイダのlistenプロパティで登録されたリスナー**
3. **イベント名に＊を用いたリスナー**（ワイルドカードイベントリスナー）


# 優先順位を調べた経緯
ちなみに、僕が優先順位について調べた経緯を書いておくと
イベントが発火された時点で実行予定のリスナー情報をログに吐けたらカッコイイかもとか思って
すべてのイベント発火で一番最初に呼ばれるリスナーを登録したかったからでした。。

結果的には大きく２つの壁にぶち当たって、お手軽に出来ない事がわかりました。

* 効率良く全てのイベントをリッスンするにはワイルドカードイベントリスナーを使いたいけど最初に実行されない。
* リスナー情報を表示したいけど、無名関数になると何をログに吐いたらいいか分からない。

# 最後に
僕自身はイベント処理みたいの使うの初めてなのですが、経験ある人の話を聞いたりして分かったのは

* イベントリスナーに優先順位を設けたくなったら**「これイベント処理にすべきなのか？」**と冷静になってみるべき。
* ワイルドカードイベントリスナーを使うと、後からコードを追う時に**イベント名で探せなくなる**ので要注意。

ググってこの記事に辿りついた人は、もしかしたら僕のように無理にイベント処理を使おうとしている人かもしれない。