【Laravel】 イベント使って処理を実行する
イベントを利用することで、イベントを送信 → 複数の場所で受け取り といった事が可能になります。イベントを使用することでコントローラとクラス間の疎結合化ができ、スマートに管理できます。
目次
イベントの構造
イベントでは、event
関数を用いて送られる情報に応じて、どのクラスに情報を送信するかを定義し、event
関数で送信すれば自動的に情報を送ってくれるという動きをします。
イベントの管理をしているのは、app/Http/Providers/EventServiceProvider.php
です。ここの$listen
で、どのイベントが来たらどのクラスにイベントを送信するかが定義されています。

イベントを動かす
イベントを作成し、実際に動作させてみましょう。
イベントとリスナーを作って紐付ける
まずはイベントを受け取るリスナーを作りましょう。artisan
で作るのが早いです。
event:generateで作る
先にイベントのクラスと情報の送信先を書いておけば、自動で生成してくれます。ここで、use
も書いておかないと全部Providers
ディレクトリ内に作られてしまうので、存在しないクラスですがuse
しておきましょう。
// ... use App\Events\MyEvent; use App\Listeners\MyListener; use App\Listeners\MyListener2; class EventServiceProvider extends ServiceProvider { /** * The event listener mappings for the application. * * @var array<class-string, array<int, class-string>> */ protected $listen = [ // event関数でMyEventを発火したら、MyListenerとMyListener2に送信される MyEvent::class => [ MyListener::class, MyListener2::class, ], ]; // ... }
定義したら、作成します。
php artisan event:generate
実行すると、app/Events
ディレクトリ内とapp/Listeners
ディレクトリ内にファイルが作成されているはずです。
use
を忘れると、全てProviders
ディレクトリ内に作成されます。
make:eventとmake:listenerで作る
上ではProviderに定義してから作りましたが、定義する前に作りたい場合や、イベントとリスナーを1つ1つ作りたい場合、make:event
とmake:listener
を使用します。
php artisan make:event MyEvent php artisan make:listener MyListener --event=MyEvent php artisan make:listener MyListener2 --event=MyEvent
イベントに値を入れられるようにする
イベントの流れでは、実質クラスのインスタンスを受け渡しするだけです。そのため、イベントのクラスに適当に値を保持できるようにしておきましょう。
class MyEvent { use Dispatchable, InteractsWithSockets, SerializesModels; private $value; public function __construct($value) { $this->value = $value; } public function getValue() { return $this->value; } }
Dispatchable
は、MyEvent::dispatch()
等をするためのものです。
SerializesModels
は、モデルの受け渡しであったほうが良いものです。キューを使用するリスナーなどでモデルをシリアライズする必要がある場合、適切にシリアライズしてくれます。
デメリットがあるわけでもないので、そのままで大丈夫です。
受け取る側を実装する
作成したListenerを見ると、handle($event)
関数があります。イベントはこのメソッドに渡されます。そのため、実行したい内容はこのhandle
メソッドに書きます。
use App\Events\MyEvent; use Illuminate\Support\Facades\Log; class MyListener { // ... public function handle(MyEvent $event) { Log::debug($event->getValue()); } }
イベントを発火する
これで、イベントとリスナーの紐付けと、イベントの内容と受け取る側の定義が完了しました。早速イベントを送信してみましょう。
送信するには、event
関数か、MyEvent::dispatch
を用います。コントローラに適当に実装してみましょう。
use App\Events\MyEvent; class HelloController extends Controller { public function sampleStore() { Log::debug('イベント送信開始'); // どちらでも良い event(new MyEvent('foobar')); MyEvent::dispatch('foobar'); Log::debug('イベント送信完了'); return 'hoge'; } }
実際にページにアクセスするなどしてコントローラを動かしてみましょう!
イベントディスカバリー
今回の例では$listen
にイベントとリスナーの対応を手動で登録していました。しかし、自動的に紐付ける方法もあります。
その場合、EventServiceProvider
側でイベントディスカバリーを有効にし、handle
または__invoke
メソッドの第1引数にタイプヒントを付けるだけです。
class EventServiceProvider extends ServiceProvider { // ... public function shouldDiscoverEvents() { return false; } }
class MyListener { /** * MyEvent $event のように、型をつける */ public function handle(MyEvent $event) { // ... } }
すでに$listener
に登録されているものに加えて、自動検出したものも実行されます。
そのため、リスナーのhandler
や__invoke
にタイプヒントを付けている場合は、そのリスナーを$listener
に登録してしまうと重複実行されることになるので注意が必要です。
リスナーが増えるとだんだん重くなってきます。そのため、本番環境ではデプロイ時にphp artisan event:cache
を実行してキャッシュを作成しておくことをオススメします。
キャッシュの上書きは同じコマンドで、キャッシュをクリアしたい場合はphp artisan event:clear
をします。
イベント登録の種類
上の例では$lister
に追加しましたが、それ以外の方法でも追加できます。
Event::listenを使う
EventServiceProviderのboot()
内で、Event::listen(Event::class, [Listener::class, 'handle'])
のように書くと、イベントとリスナーの対応を追加できます。handle
は、呼び出したいメソッドです。
または、リスナーに対応する関数を直接入れます。
class EventServiceProvider extends ServiceProvider { public function boot() { Event::listen( MyEvent::class, [MyListener::class, 'handle'], ); Event::listen( MyEvent2::class, function (MyEvent2 $event) { Log::debug('event2'); } ); } }
ここで、関数を直接入れた場合、タイプヒントをつければ自動的にイベントとの紐付けを作成してくれるため、関数だけでも動作します。
Event::listen( function (MyEvent2 $event) { Log::debug('event2'); } );
イベントを使うと何が良いのか
どのようなイベントでも、全部コントローラにコードを並べてしまうことも可能です。ではイベントを使うと何が良いのでしょうか。
- コントローラやサービスがスリムになる
- 同じイベントを使用した機能追加や変更時の影響を最小限にできる
1つ目はコードの所在が変わることによるものです。コントローラやサービスに書いていた処理をリスナーに移すことになります。
2つ目は疎結合化によるものです。普通に書くとコントローラが直接処理部分のメソッドを呼び出したりします。
例えば、今までEmailで通知していたのをSlackへの通知に切り替えたとします。通常であればメソッド名の変更で大量のコードが書き換わるかもしれません。そうするとテストが大変です。置換ミスもあるかもしれません。
しかし、イベントを経由することで、呼び出すリスナーを変更するだけで済みます。つまり、影響をProvider
だけに留めることが可能になります。また、slackとemail同時に送信することも簡単に改修可能です。
まとめ
Laravelのイベントはオブザーバーパターンの1つです。イベントを送信し、リスナーがそのイベントを受け取ります。イベントとリスナーの紐付けはEventServiceProvider
で行います。
次はキューを使用したイベントも見てみましょう。
