序章

Laravelは間違いなく非常に強力なフレームワークであり、多くのバッテリーが含まれています。 私がLaravelを気に入っている理由の1つは、Laravelがイベントに基づいて構築されているという事実です。

Laravelイベントとは何ですか?

イベントは、プログラムによって処理される可能性のあるプログラムによって認識されるアクションまたは発生です。 Laravelでのイベントの例は次のとおりです。

  • 新しいユーザーがサインアップしました
  • コメントが投稿されました
  • ユーザーがブログ投稿を作成する
  • ユーザーは写真が好きです
  • もっと…

なぜイベントを使用するのですか?

イベントを使用する理由は、イベントによってアプリケーションの懸念事項を分離し、アプリケーションのアクションにフックするためのメカニズムを作成できるためです。 イベントが発生した場合、イベントはその実装について何も知る必要はありません。イベントが知る必要があるのは、アクションが実行され、イベントがトリガーされ、イベントがリスナーまたはサブスクライバーにデータを送信することだけです。それに対処します。

イベントがなければ、1か所にたくさんのロジックがあります

イベントなしでアプリがどのように見えるかを示すために、次のようなことを行います。

    Route::get('signup', function() {
      // create the new user
      $user = User::create($userData);

      // a user is created, do things!
      // send the user a confirmation email
      // set up that users account
      // email them introductory information
      // do more stuff
    });

リストが少し長くなり始める方法がわかります。 これをイベントに分離して、多くのロジックをイベント自体から分離することができます。

イベントを使用するもう1つの理由は、アプリケーションのデータ処理を高速化するためです。

注: Pub Sub に既に精通している場合、イベントは新しい概念ではありません。

イベントを使用するタイミング

引き続きソーシャルブログアプリケーションを使用している場合、ユーザーがブログ投稿を作成すると、次のようなチェックリストが作成される可能性があります。

  • REST APIを使用して、盗用の投稿を確認します。
  • 新しい投稿をフォロワーに通知します。
  • ソーシャルネットワークや検索エンジンなどに投稿を送信します。

このようなライニングアクションはアプリケーションの速度を低下させる可能性があり、イベントを使用してこのボトルネックを取り除くことができます。

Laravelは、次のような多くのシステムイベントもトリガーします。

  • Laravelは、EloquentがCRUD操作を実行するたびにイベントをトリガーします。
  • Laravelがブレードテンプレートをコンパイルすると、イベントが発生します。

これらは、Laravelによって発生したイベントのほんの一部です。

イベントの定義

Laravelでイベントを生成するには、古き良きartisanを使用するだけです。

  1. php artisan make:event ActionDone

これにより、次の名前のクラスが作成されます ActionDoneapp/Events ディレクトリ、そしてそれはこのように見えるはずです:

    <?php namespace App\Events;

    use App\Events\Event;
    use Illuminate\Queue\SerializesModels;
    use Illuminate\Contracts\Broadcasting\ShouldBroadcast;

    class ActionDone extends Event {

        use SerializesModels;

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

        /**
         * Get the channels the event should be broadcast on.
         *
         * @return array
         */
        public function broadcastOn()
        {
            return [];
        }
    }

イベントの作成は非常に簡単で、セットアップはほとんど必要ありません。

イベントリスナー(イベントが発生したとき)

イベントがトリガーされた後、アプリケーションはイベントの処理方法を知る必要があり、そのためにはリスナーが必要です。 Laravelでリスナーを生成するには、職人を使用するだけです。

  1. php artisan make:listener ThingToDoAfterEventWasFired --event="ActionDone"

イベントのリスナーを生成するコマンドは、必要なものを取り込みます --event リッスンするイベントの名前が付いたフラグ。 上記のコマンドは、という名前のクラスを作成します ThingToDoAfterEventWasFiredapp/Listeners、およびが含まれています。

    <?php namespace App\Listeners;

    use App\Events\ActionDone;
    use Illuminate\Queue\InteractsWithQueue;
    use Illuminate\Contracts\Queue\ShouldQueue;

    class ThingToDoAfterEventWasFired {

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

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

イベントリスナークラスでは、handleメソッドはLaravelによってプログラムでトリガーされ、トリガーされたイベントのクラスをタイプヒントします。

イベントの登録

あちこちでイベントを発生させる前に、Laravelのコマンドバスはイベントとそのリスナー、およびそれらの処理方法について知る必要があります。

イベントを登録するには、次の場所に移動します app/Providers/EventServiceProvider.php、保護されたlistenプロパティが EventServiceProvider クラスと:

    protected $listen = [
        'App\Events\ActionDone' => [
            'App\Listeners\ThingToDoAfterEventWasFired',
        ],
    ];

コマンドバスにイベントとそのリスナーについて通知するために必要なのはこれだけです。 リスナーの配列があることに注意してください。つまり、イベントには複数のサブスクライバーを含めることができます。

イベントを複数のリスナーから発生させるには、その配列に追加するだけです。

    protected $listen = [
        'App\Events\ActionDone' => [
            'App\Listeners\ThingToDoAfterEventWasFired',
            'App\Listeners\OtherThingToDoAfterEventWasFired',
            'App\Listeners\AnotherThingToDoAfterEventWasFired',
        ],
    ];

イベントサブスクライバー

イベントサブスクライバーは、クラス自体から複数のイベントをサブスクライブできるクラスであり、単一のクラス内で複数のイベントハンドラーを定義できます。 上記の例では、イベント内のイベントのリスナーに明示的に名前を付けています。 EventServiceProvider.php ファイル。 新しいイベントとリスナーを定義します UserEventListener.php ファイル。

サブスクライバーは、サブスクライブメソッドを定義する必要があります。このメソッドには、イベントディスパッチャーインスタンスが渡されます。

    <?php namespace App\Listeners;

    class UserEventListener {

        /**
         * Handle user login events.
         */
        public function onUserLogin($event) {}

        /**
         * Handle user logout events.
         */
        public function onUserLogout($event) {}

        public function subscribe($events)
        {
            $events->listen(
                'App\Events\UserLoggedIn',
                'App\Listeners\UserEventListener@onUserLogin'
            );

            $events->listen(
                'App\Events\UserLoggedOut',
                'App\Listeners\UserEventListener@onUserLogout'
            );
        }
    }

イベントサブスクライバーを登録するには、に戻って EventServiceProvider クラス、およびsubscribeプロパティにサブスクライバークラスを追加します。

    protected $subscribe = [
        'App\Listeners\UserEventListener',
    ];

これで、で作成されたすべてのイベントとリスナー UserEventListener 動作します。

イベントの発送

イベントを発生させるには、 Event イベントを開始するファサード

    use Event;
    use App\Events\ActionDone;

    ...

    Event::fire(new ActionDone());

または、 event イベントを発生させるヘルパーメソッド

    use App\Events\ActionDone;

    ...

    event(new ActionDone());

リスナーにデータを渡すには、イベントクラスをDTOとして使用するだけです。

キューに入れられたイベントリスナー

イベント/リスナーがアプリケーションの処理を遅らせたくない場合があります。 たとえば、ユーザーが新しいアカウントを作成するためのリクエストを送信している間、ユーザーが新しいユーザーのサインアップメールがメールで送信されるのを待つ必要はありません。

アプリケーションキューにイベントリスナーを追加できます。これは、指定したキュードライバーによって処理されます。 これは私たちのアプリの処理を妨げることはありません。 これを行うには、Listenerクラスに実装させるだけです。 Illuminate\Contracts\Queue\ShouldQueue.

    <?php namespace App\Listeners;

    use App\Events\ActionDone;
    use Illuminate\Queue\InteractsWithQueue;
    use Illuminate\Contracts\Queue\ShouldQueue;

    class ThingToDoAfterEventWasFired implements ShouldQueue { /*...*/ }

リアルタイムでのイベントのブロードキャスト

最近の多くのWebアプリケーションでは、WebSocketを使用して、リアルタイムのライブ更新ユーザーインターフェイスを実装しています。 サーバー上で一部のデータが更新されると、通常、メッセージはWebSocket接続を介して送信され、クライアントによって処理されます。

イベントをブロードキャスト可能にするには、イベントクラスに Illuminate\Contracts\Queue\ShouldBroadcast

    <?php namespace App\Events;

    use App\Events\Event;
    use Illuminate\Queue\SerializesModels;
    use Illuminate\Contracts\Broadcasting\ShouldBroadcast;

    class ActionDone extends Event implements ShouldBroadcast { /*...*/ }

The ShouldBroadcast インターフェイスには、イベントクラスが実装する必要があります broadcastOn イベントがブロードキャストする必要のあるチャネルの配列を返すメソッド。

    public function broadcastOn()
    {
        return ['action-did-occur'];
    }

デフォルトでは、Laravelはイベントのパブリックプロパティをシリアル化し、JSONとしてクライアントに送信します。 クライアントに送信される内容をより細かく制御したい場合は、 broadcastWith メソッドをイベントクラスに渡し、JSONに変換するデータを返します。

    public function broadcastWith()
    {
        return [
            'user' => [
                'name' => 'Klark Cent',
                'age' => 30,
                'planet' => 'Crypton',
                'abilities' => 'Bashing'
            ]
        ];
    }

現在、Laravelにはクライアントでのデータ消費を支援する2つのドライバー(pushersocket.io)しかありません。 この記事の範囲では、プッシャーを使用します。

プッシャーはサービスとしてのWebSocketであり、アプリケーションからサービスにリクエストを送信し、プッシャーはすべてのクライアントにメッセージをブロードキャストします。

プッシャーを使用してクライアントでデータを消費するには、次のようにします。

    var pusher = new Pusher('pusher-key');
    var channel = pusher.subscribe('action-did-occur');

    channel.bind('App\Events\ActionDone', function(data) {
       console.log(data);
    });

:必要です pusherページ上ののJavaScriptSDK:

    <script src="//js.pusher.com/2.2/pusher.min.js"></script>

あなたも必要です pusherを介して利用可能なのPHPSDK composer:

  1. composer require pusher/pusher-php-server:~2.0

シャウトボックスの作成

デモアプリケーションはシンプルなシャウトボックスで、ユーザーはフォームにTwitterハンドル、メールアドレス、およびシャウトしたい内容を入力します。

を構成する .env ファイル、私のものは次のようになります:

    APP_ENV=local
    APP_DEBUG=true
    APP_KEY=APP_KEY

    DB_HOST=localhost
    DB_DATABASE=scotchbox
    DB_USERNAME=root
    DB_PASSWORD=password

    PUSHER_KEY=PUSHER_KEY
    PUSHER_SECRET=PUSHER_SECRET
    PUSHER_APP_ID=PUSHER_APP_ID

詳細については .env ファイルとその機能については、以下をお読みください。Laravel環境変数について

プッシャーは、アプリケーションのリアルタイムコンポーネントを簡単にする優れたサービスです。 これらは有料サービスですが、無料のサンドボックスオプション(最大接続数20、1日あたり10万メッセージ)があり、小さなアプリケーションや開発だけで十分です。 先に進み、 Pusher でアカウントを作成して、資格情報を取得します。

データベースの設定

データベースを準備して、 shoutouts. を作成する必要があります shoutouts 移行Eloquentモデルを使用したテーブル。

プロジェクトのルートにあるコマンドラインから次のコマンドを実行します。

  1. php artisan make:migration create_shoutouts_table --create=shoutouts && php artisan make:model Models/Shoutout

職人によって作成された移行(database/migrations/create_shoutouts_table.php)が含まれています:

    $table->increments('id');
    $table->string('handle');
    $table->string('email');
    $table->text('content');
    $table->timestamps();

モデルは保護されている必要があります fillabletable プロパティ

        /**
         * The database table used by the model.
         *
         * @var string
        */
        protected $table = 'shoutouts';

        /**
         * The attributes that are mass assignable.
         *
         * @var array
        */
        protected $fillable = ['handle', 'email', 'content'];

ルーティングとコントローラー

次に、ルーティングを設定し、コントローラーの作成に進みます。 私たちの中で app\Http\routes.php ファイル、リソースルートを作成します。

    Route::resource('shoutouts', 'SiteController');

ルートがマップされたので、次を作成してみましょう。 SiteController 職人を使用します。

  1. php artisan make:controller SiteController

このリソースルートは、CRUD操作に必要なルートを含む、アプリケーションのいくつかのルートを自動的に作成します。 作成されたルートを確認するには、次を使用します。

  1. php artisan route:list

シャウトボックスの作成の処理

The store 新しく作成されたメソッド SiteController シャウトボックスの作成とデータベースへの保存を処理する場所になります。 私たちのコントローラーで store メソッドは、以下を追加しましょう:

    <?php namespace App\Http\Controllers;

    ...

    use App\Models\Shoutbox;

    public function store(Request $request) {
        $validator = Validator::make($request->all(), Shoutout::$rules);

        /**
         * Try validating the request
         * If validation failed
         * Return the validator's errors with 422 HTTP status code
        */
        if ($validator->fails())
        {
            return response($validator->messages(), 422);
        }

        $shoutout = Shoutout::create($request->only('email', 'handle', 'content'));

        // fire ShoutoutAdded event if shoutout successfully added to database
        event(new ShoutoutAdded($shoutout));

        return response($shoutout, 201);
    }

クライアント側(JavaScript)で、Pusherに接続し、 App\Events\ShoutoutAdded イベント。

    var notifyUser = function (data) {
        var data = data.shoutout;

        if (! ('Notification' in window)) {
            alert('Web Notification is not supported');

            return;
        }

        Notification.requestPermission(function(permission){
            var notification = new Notification('@'+ data.handle +' said:', {
                body: data.content,
                icon: document.getElementById('site_image').content
        });
    };

    var loadPusher = function (){
        Pusher.log = function(message) {
            if (window.console && window.console.log) {
                window.console.log(message);
            }
        };

        var pusher = new Pusher(document.getElementById('pusher_key').content);
        var channel = pusher.subscribe('shoutout-added');

        channel.bind('App\\Events\\ShoutoutAdded', notifyUser);
    };

クライアントはブロードキャストされたイベントをリッスンし、 Notification API を使用して、現在サイトにいるすべてのユーザーに警告します。 一連のイベントを発生させるために必要なのは、コントローラーで次の行を使用することだけでした。

    // fire ShoutoutAdded event if shoutout successfully added to the database
    event(new ShoutoutAdded($shoutout));

アプリケーションロジックをコントローラーからイベントリスナーに分離する方法がわかります。 それは私たちのアプリケーションを無駄のない、意味のあるものに保ちます。

結論

うまくいけば、これは、イベントがいかに簡単に使用でき、どのようにアプリケーションを合理化できるかを理解するのに役立ちます。

イベントは、アクションが発生したことをアプリケーションに通知します。 これらは誤用されるべきではありません。すべての操作にイベントが必要なわけではありませんが、肥大化したコントローラーから多くのロジックを移動できる場合は、より適切に編成されます。