📘

Laravelのイベントを処理を復習してみた。

2024/11/09に公開

まえがき

WEB系プログラマーなら誰でもイベント処理を書くことはあると思います。
javascriptのonclickイベントとか、コピペを使いながら頑張って書いているのではないでしょうか。

このイベント処理、用意されているイベントのリスナー部分を書くのは結構初心者のうちからやるのでわかりやすいですが、独自のイベントを作るのはかなり難しいと思います。

Laravelのイベントの起こし方はいくつか記事があるので見様見真似で実装できなくはないのですが、それぞれのクラスの意味・役割をキッチリ理解するのはハードルが高いと思います。

そこで自分なりに解釈して書いてみようと思いました。

そもそもイベントってなんだ

イベント処理は平たく言えば、「〇〇が起こった時、△△をする」という関係性が一番しっくりくるかなと思います。
「ボタンが押された時、ダイアログを出す。」とか「購入が完了した時、メールを送る。」とか「ステータスが変わった時、入力欄を非活性にする。」とか。

とはいえ全部がそうかというと、「ページにアクセスした時、画面にHTMLを表示する。」とかはイベントとは呼びませんね。
ただのリクエスト・レスポンスの関係です。

イベントは「◯◯が起きたときにそれをシステムに通知する」仕組みも備えている必要がありそうです。
ここで△△を決めるのがリスナーと言っていいでしょう。

オブザーバーパターンとの関係性

Laravelのイベントは、GOFのデザインパターンのオブザーバーパターンを元にしています。

オブザーバーパターン Laravel
イベント=通知の内容 Eventクラス
観察者(購読者) Listenerクラス
イベントディスパッチャ・サブジェクト eventヘルパー関数・Dispatcherクラス

こんな感じで発行者を引数に渡してイベント発火すると購読者に通知されて、Listenerクラスの中の処理が発動するわけですね。

 event(new UpdatedTicketEvent($ticket, $ticketComment));

event()関数は、実際にはIlluminate\Events\Dispatcherクラスのdispatchメソッドを呼び出しています。

Illuminate\Events\Dispatcher.php
    /**
     * Fire an event and call the listeners.
     *
     * @param  string|object  $event
     * @param  mixed  $payload
     * @param  bool  $halt
     * @return array|null
     */
    public function dispatch($event, $payload = [], $halt = false)
    {
        // When the given "event" is actually an object we will assume it is an event
        // object and use the class as the event name and this event itself as the
        // payload to the handler, which makes object based events quite simple.
        [$event, $payload] = $this->parseEventAndPayload(
            $event, $payload
        );

        if ($this->shouldBroadcast($payload)) {
            $this->broadcastEvent($payload[0]);
        }

        $responses = [];

        foreach ($this->getListeners($event) as $listener) {
            $response = $listener($event, $payload);

            // If a response is returned from the listener and event halting is enabled
            // we will just return this response, and not call the rest of the event
            // listeners. Otherwise we will add the response on the response list.
            if ($halt && ! is_null($response)) {
                return $response;
            }

            // If a boolean false is returned from a listener, we will stop propagating
            // the event to any further listeners down in the chain, else we keep on
            // looping through the listeners and firing every one in our sequence.
            if ($response === false) {
                break;
            }

            $responses[] = $response;
        }

        return $halt ? null : $responses;
    }

この中のgetListenersでEventServiceProviderに登録したリスナーのうち、該当するイベントに紐づくものが入ってきます。

        foreach ($this->getListeners($event) as $listener) {
            $response = $listener($event, $payload);

で、$listenerの中身がリスナークラス名かクロージャになっていて、リフレクションにより処理が発動します。

他言語との違い

Javaなどでは裏で別スレッドが動いていて、それでイベント通知を検知する仕組みが使われるパターンがあります。
言語やフレームワークの特性によってもDispatcherの実装方法は違うようです。

おわりに

いかがだったでしょうか?
結構難しかったと思います。
Dispatcherクラスなんて知らなくてもいいので書いている記事はないかもしれません。
しかしLaravelが自動で何をやってくれているかを少しは見ていかないと、データが魔法のようにワープして見えるので処理を追いかけづらいと思います。
奥が深いのでもしかしたらオブザーバーに焦点をあてて続編が出るかもしれません。

株式会社ONE WEDGE

【Serverlessで世の中をもっと楽しく】 ONE WEDGEはServerlessシステム開発を中核技術としてWeb系システム開発、AWS/GCPを利用した業務システム・サービス開発、PWAを用いたモバイル開発、Alexaスキル開発など、元気と技術力を武器にお客様に真摯に向き合う価値創造企業です。
https://onewedge.co.jp

Discussion