Laravelイベントでリアルタイムのシャウトボックスを作成する
序章
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を使用するだけです。
- php artisan make:event ActionDone
これにより、次の名前のクラスが作成されます ActionDone
の app/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でリスナーを生成するには、職人を使用するだけです。
- php artisan make:listener ThingToDoAfterEventWasFired --event="ActionDone"
イベントのリスナーを生成するコマンドは、必要なものを取り込みます --event
リッスンするイベントの名前が付いたフラグ。 上記のコマンドは、という名前のクラスを作成します ThingToDoAfterEventWasFired
の app/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つのドライバー(pusherとsocket.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
:
- 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モデルを使用したテーブル。
プロジェクトのルートにあるコマンドラインから次のコマンドを実行します。
- 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();
モデルは保護されている必要があります fillable
と table
プロパティ
/**
* 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
職人を使用します。
- php artisan make:controller SiteController
このリソースルートは、CRUD操作に必要なルートを含む、アプリケーションのいくつかのルートを自動的に作成します。 作成されたルートを確認するには、次を使用します。
- 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));
アプリケーションロジックをコントローラーからイベントリスナーに分離する方法がわかります。 それは私たちのアプリケーションを無駄のない、意味のあるものに保ちます。
結論
うまくいけば、これは、イベントがいかに簡単に使用でき、どのようにアプリケーションを合理化できるかを理解するのに役立ちます。
イベントは、アクションが発生したことをアプリケーションに通知します。 これらは誤用されるべきではありません。すべての操作にイベントが必要なわけではありませんが、肥大化したコントローラーから多くのロジックを移動できる場合は、より適切に編成されます。