このリポジトリはPHP Webアプリフレームワークである、LaravelのLTSバージョンである5.5の公式英文ドキュメントを日本語へ翻訳しています。
This project is maintained by okinaka
多くの近代的なアプリケーションでは、リアルタイムでライブ更新されるユーザーインターフェイスを実装するために、WebSocketが使用されています。サーバ上で何かのデータが更新されると、通常WebSocket接続を通じメッセージが送信され、クライアントにより処理されます。これは変更をアプリケーションに対しポーリングし続ける方法よりも、強固で効率的です。
こうしたタイプのアプリケーション構築を援助するため、LaravelはWebSocket接続経由で、イベントを簡単に「ブロードキャスト」できます。Laravelでイベントをブロードキャストすることにより、サーバサイドのコードとクライアントサイドのJavaScriptで、同じ名前のイベントを共有できます。
{tip} ブロードキャストを開始する前に、Laravelのイベントとリスナに関するドキュメントをすべてしっかりと読んでください。
イベントブロードキャストの設定オプションは、すべてconfig/broadcasting.php設定ファイルの中にあります。Laravelはドライバをいくつか準備しています。PusherやRedis、それにローカルの開発とデバッグのためのlogドライバがあります。さらにブロードキャストを完全に無効にするための、nullドライバも用意しています。config/broadcasting.php設定ファイルに、各ドライバの設定例が含まれています。
イベントをブロードキャストするには、事前にApp\Providers\BroadcastServiceProviderを登録する必要があります。インストールしたばかりのLaravelアプリケーションで、config/app.php設定ファイル中の、providers配列配列にある、このプロバイダのコメントを外してください。このプロバイダはブロードキャスト認証ルートとコールバックを登録します。
Laravel Echoは、現在のセッションのCSRFトークンへアクセスする必要があります。アプリケーションのhead HTML要素を確認し、SCRFトークンを含むようにmetaタグを定義してください。
<meta name="csrf-token" content="">
イベントをPusherによりブロードキャストする場合、Composerパッケージマネージャを使い、Pusher PHP SDKをインストールする必要があります。
composer require pusher/pusher-php-server "~3.0"
次に、Pusherの認証情報をconfig/broadcasting.php設定ファイル中で設定する必要があります。Pusherの設定例はこのファイルに含まれ、Pusherキーと秘密キー、アプリケーションIDを簡単に指定できます。config/broadcasting.phpファイルのpusher設定では、Pusherでサポートされているクラスタなど、追加のオプション(options)も設定可能です。
'options' => [
'cluster' => 'eu',
'encrypted' => true
],
PusherとLaravel Echoを使用する場合、resources/assets/js/bootstrap.jsファイルのEchoインスタンスをインスタンス化する時に、使用するブロードキャスタとして、pusherを指定する必要があります。
import Echo from "laravel-echo"
window.Pusher = require('pusher-js');
window.Echo = new Echo({
broadcaster: 'pusher',
key: 'your-pusher-key'
});
Redisブロードキャスタを使用する場合は、Predisライブラリをインストールする必要があります。
composer require predis/predis
RedisブロードキャスタはRedisのpub/sub機能を使用し、メッセージをブロードキャストします。Redisからのメッセージを受け、WebSocketチャンネルへブロードキャストできるように、これをWebSocketとペアリングする必要があります。
Redisブロードキャスタがイベントを発行すると、そのイベントに指定されたチャンネル名へ発行され、イベント名、dataペイロード、イベントのソケットIDを生成したユーザー(該当する場合)を含む、ペイロードはJSONエンコードされた文字列になります。
RedisブロードキャスタとSocket.IOサーバをペアリングする場合、アプリケーションのhead HTML要素で、Socket.IO JavaScriptクライアントライブラリをインクルードする必要があります。Socket.IOサーバがスタートすると、自動的にクライアントJavaScriptライブラリを標準的なURLとして公開します。たとえば、Socket.IOサーバをWebアプリケーションのあるドメインで実行しているなら、以下のようにクライアントライブラリへアクセスできます。
<script src="//:6001/socket.io/socket.io.js"></script>
次に、socket.ioコネクタとhostを指定し、Echoをインスタンス化します。
import Echo from "laravel-echo"
window.Echo = new Echo({
broadcaster: 'socket.io',
host: window.location.hostname + ':6001'
});
最後に、Socket.IOのコンパチブルサーバを実行する必要があります。LaravelにはSocket.IOサーバの実装は含まれていません。しかし、tlaverdure/laravel-echo-server GitHubリポジトリで、コミュニティにより現在、Socket.IOサーバがメンテナンスされています。
イベントをブロードキャストし始める前に、キューリスナを設定し、実行する必要もあります。イベントのブロードキャストは、すべてキュージョブとして行われるため、アプリケーションのレスポンスタイムにはシリアスな影響はでません。
Laravelのイベントブロードキャストは、サーバサイドのLaravelイベントから、WebSocketに対する駆動ベースのアプローチを使っている、あなたのクライアントサイドのJavaScriptアプリケーションへ、ブロードキャストできるようにします。現在、PusherとRedisドライバーが用意されています。Laravel Echo JavaScriptパッケージを使用したクライアントサイド上で、イベントは簡単に利用できます。
パブリック、もしくはプライベートに指定された「チャンネル」上で、イベントはブロードキャストされます。アプリケーションの全訪問者は、認証も認可も必要ないパブリックチャンネルを購入できます。しかし、プライベートチャンネルを購入するためには、認証され、そのチャンネルをリッスンできる認可が必要です。
イベントブロードキャストの各コンポーネントへ飛び込む前に、例としてeコマースショップを使い、ハイレベルな概念を把握しましょう。このドキュメント中の別のセクションで詳細を説明するため、PusherとLaravel Echoの設定についての詳細は省きます。
このアプリケーションでは、ユーザーに注文の発送状態を確認してもらうビューページがあるとしましょう。さらに、アプリケーションが発送状態を変更すると、ShippingStatusUpdatedイベントが発行されるとしましょう。
event(new ShippingStatusUpdated($update));
ShouldBroadcastインターフェイスユーザーがある注文を閲覧している時に、ビューの状態を変更するために、ユーザーがページを再読込しなくてはならないなんてしたくありません。代わりにアップデートがあることをアプリケーションへブロードキャストしたいわけです。そのため、ShouldBroadcastインターフェイスを実装した、ShippingStatusUpdatedイベントを作成する必要があります。このインターフェイスはイベントが発行されると、ブロードキャストすることをLaravelへ指示しています。
<?php
namespace App\Events;
use Illuminate\Broadcasting\Channel;
use Illuminate\Queue\SerializesModels;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
class ShippingStatusUpdated implements ShouldBroadcast
{
/**
* 発送状態更新の情報
*
* @var string
*/
public $update;
}
ShouldBroadcastインターフェイスはイベントで、broadcastOnメソッドを定義することを求めています。このメソッドはイベントをブロードキャストすべきチャンネルを返す責任を持っています。イベントクラスを生成すると、既にこのメソッドはからのスタブクラスに作成されていますので、詳細を埋めるだけになっています。オーダーの発注者だけに状態の変更を見てもらいたいので、そのオーダーに紐付いたプライベートチャンネルへ、イベントをブロードキャストしましょう。
/**
* イベントをブロードキャストすべき、チャンネルの取得
*
* @return array
*/
public function broadcastOn()
{
return new PrivateChannel('order.'.$this->update->order_id);
}
プライベートチャンネルをリッスンするには、ユーザーは認可されている必要があることを思い出してください。routes/channels.phpファイルでチャンネルの認可ルールを定義してください。この例の場合、プライベートorder.1チャンネルをリッスンしようとするユーザーは、実際にそのオーダーの発注者であることを確認しています。
Broadcast::channel('order.{orderId}', function ($user, $orderId) {
return $user->id === Order::findOrNew($orderId)->user_id;
});
channelメソッドは引数を2つ取ります。チャンネルの名前と、ユーザーにそのチャネルをリッスンする認可があるかどうかをtrueかfalseで返すコールバックです。
認可コールバックは、最初の引数に現在認証中のユーザーを受け取ります。引き続き、追加のプレースホルダパラメータを指定します。この例の場合、チャンネル名中で”ID”の部分を表す、{orderID}プレースホルダーを使っています。
次に、皆さんのJavaScriptアプリケーションでイベントをリッスンします。このために、Laravel Echoが利用できます。最初に、プライベートチャンネルを購読するために、privateメソッドを使います。それから、ShippingStatusUpdatedイベントをリッスンするために、listenメソッドを使用します。デフォルトでは、イベントのpublicプロパティは、すべてブロードキャストイベントに含まれています。
Echo.private('order.' + orderId)
.listen('ShippingStatusUpdated', (e) => {
console.log(e.update);
});
Laravelへイベントをブロードキャストすることを知らせるためには、そのイベントクラスでIlluminate\Contracts\Broadcasting\ShouldBroadcastインターフェイスを実装します。このインターフェイスは、フレームワークにより生成されたすべてのイベントクラスで、useされていますので、イベントへ簡単に追加できます。
ShouldBroadcastインターフェイスは、broadcastOnメソッド一つのみ実装を求めています。broadcastOnメソッドは、そのイベントをブロードキャストすべきチャンネルか、チャンネルの配列を返します。チャンネルはChannel、PrivateChannel、PresenceChannelのインスタンスです。Channelインスタンスはユーザーが行動するパブリックチャンネルを表しています。一方、PrivateChannelとPresenceChannelは、チャンネル認可が必要な、プライベートチャンネルを表しています。
<?php
namespace App\Events;
use Illuminate\Broadcasting\Channel;
use Illuminate\Queue\SerializesModels;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
class ServerCreated implements ShouldBroadcast
{
use SerializesModels;
public $user;
/**
* 新しいイベントインスタンスの生成
*
* @return void
*/
public function __construct(User $user)
{
$this->user = $user;
}
/**
* イベントをブロードキャストすべき、チャンネルの取得
*
* @return Channel|array
*/
public function broadcastOn()
{
return new PrivateChannel('user.'.$this->user->id);
}
}
これで、あと必要なのは、通常通りにイベントを発行するだけです。イベントを発行すると、キュージョブが指定済みのドライバを通して、自動的にそのイベントをブロードキャストします。
デフォルトでLaravelはイベントのクラス名を使い、そのイベントをブロードキャストします。イベントにbroadcastAsメソッドを定義することにより、ブロードキャスト名をカスタマイズできます。
/**
* イベントブロードキャスト名
*
* @return string
*/
public function broadcastAs()
{
return 'server.created';
}
broadcastAsメソッドを使い、ブロードキャスト名をカスタマイズする場合、.文字を先頭に付けたリスナを登録するのを忘れないでください。これによりそのエベントへ、アプリケーションの名前空間を付けないよう、Echoに指示します。
.listen('.server.created', function (e) {
....
});
イベントがブロードキャストされると、イベントのペイロードとしてpublicプロパティはすべて自動的にシリアライズされます。これによりJavaScriptアプリケーションより、publicデータにアクセスできます。ですから、たとえば、あるイベントにEloquentモデルを含む、publicの$userプロパティがあれば、そのイベントのブロードキャストペイロードは、次のようになります。
{
"user": {
"id": 1,
"name": "Patrick Stewart"
...
}
}
しかしながら、ブロードキャストペイロードをより上手くコントロールしたければ、そのイベントへbroadcastWithメソッドを追加してください。このメソッドから、イベントペイロードとしてブロードキャストしたいデータの配列を返してください。
/**
* ブロードキャストするデータを取得
*
* @return array
*/
public function broadcastWith()
{
return ['id' => $this->user->id];
}
デフォルトでは各ブロードキャストイベントは、queue.php設定ファイルで指定されているデフォルトキュー接続の、デフォルトキューへ投入されます。イベントクラスのbroadcastQueueプロパティを定義することにより、使用するキューをカスタマイズできます。このプロパティには、ブロードキャスト時に使用したいキューの名前を指定してください。
/**
* イベントを投入するキューの名前
*
* @var string
*/
public $broadcastQueue = 'your-queue-name';
デフォルトキュードライバーの代わりに、syncキューを使いイベントをブロードキャストする場合、ShouldBroadcastの代わりにShouldBroadcastNowインターフェイスを実装してください。
<?php
use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;
class ShippingStatusUpdated implements ShouldBroadcastNow
{
//
} <a name="broadcast-conditions"></a> ### ブロードキャスト条件
指定した条件がtrueの場合のみ、ブロードキャストを行いたい場合もあるでしょう。イベントクラスへ、broadcastWhenメソッドを追加すれば、こうした条件を定義できます。
/**
* このイベントでブロードキャストするかを決定
*
* @return bool
*/
public function broadcastWhen()
{
return $this->value > 100;
}
プライベートチャンネルでは、現在の認証ユーザーが実際にそのチャンネルをリッスンできるか、認可する必要があります。これは、Laravelアプリケーションへチャンネル名を含めたHTTPリクエストを作成し、アプリケーションにそのユーザーが、そのチャンネルをリッスンできるかを決めさせることで実現します。Laravel Echoを使用する場合、プライベートチャンネルへの購入許可HTTPリクエストは、自動的に作成されます。しかし、そうしたリクエストに対してレスポンスする、ルートを確実に定義する必要があります。
嬉しいことに、Laravelでは、チャンネル認可にクエストに対するレスポンスのルート定義も簡単です。Laravelアプリケーションに含まれているBroadcastServiceProviderで、Broadcast::routesメソッドが呼びだされているのが見つかります。このメソッドが認可リクエストを処理する、/broadcasting/authルートを登録しています。
Broadcast::routes();
Broadcast::routesメソッドは自動的に、そのルートをwebミドルウェアグループの中に設置しますが、割り付ける属性をカスタマイズしたければ、メソッドへルート属性の配列を渡すことができます。
Broadcast::routes($attributes);
次に、チャンネル認可を実際に行うロジックを定義する必要があります。アプリケーションに含まれる、routes/channels.phpファイルで行います。このメソッドの中で、Broadcast::channelメソッドを使い、チャンネル認可コールバックを登録します。
Broadcast::channel('order.{orderId}', function ($user, $orderId) {
return $user->id === Order::findOrNew($orderId)->user_id;
});
channelメソッドは引数を2つ取ります。チャンネルの名前と、ユーザーにそのチャネルをリッスンする認可があるかどうかをtrueかfalseで返すコールバックです。
認可コールバックは、最初の引数に現在認証中のユーザーを受け取ります。引き続き、追加のプレースホルダパラメータを指定します。この例の場合、チャンネル名中で”ID”の部分を表す、{orderID}プレースホルダーを使っています。
HTTPルートと同様にチャンネルルートでも、暗黙あるいは明白なルートモデル結合を利用できます。たとえば、文字列や数値の注文IDを受け取る代わりに、実際のOrderモデルインスタンスを要求できます。
use App\Order;
Broadcast::channel('order.{order}', function ($user, Order $order) {
return $user->id === $order->user_id;
});
イベントを定義し、ShouldBroadcastインターフェイスを実装したら、あとはevent関数を使い、イベントを発行するだけです。イベントディスパッチャは、そのイベントがShouldBroadcastインターフェイスにより印付けられていることに注目しており、ブロードキャストするためにイベントをキューへ投入します。
event(new ShippingStatusUpdated($update));
イベントブロードキャストを使用するアプリケーションを構築しているとき、event関数をbroadcast関数へ置き換えることもできます。event関数と同様に、broadcast関数もイベントをサーバサイドリスナへディスパッチします。
broadcast(new ShippingStatusUpdated($update));
しかし、broadcast関数には、ブロードキャストの受取人から現在のユーザーを除外できる、toOthersメソッドが用意されています。
broadcast(new ShippingStatusUpdated($update))->toOthers();
toOthersメソッドをいつ使うのかをよく理解してもらうため、タスク名を入力してもらうことで、新しいタスクをユーザーが作成できる、タスクリストアプリケーションを想像してください。タスクを作成するためにアプリケーションは、タスクの生成をブロードキャストし、新しいタスクのJSON表現を返す、/taskエンドポイントへリクエストを作成するでしょう。JavaScriptアプリケーションがそのエンドポイントからレスポンスを受け取る時、その新しいタスクをタスクリストへ直接挿入するでしょう。次のようにです。
axios.post('/task', task)
.then((response) => {
this.tasks.push(response.data);
});
しかし、タスク生成のブロードキャストも行うことを思い出してください。もし、JavaScriptアプリケーションがタスクをリストへ追加するため、このイベントをリッスンしていれば、二重にタスクがリストへ追加されるでしょう。一つはエンドポイントから、もう一つはブロードキャストからです。
これを解決するために、toOthersメソッドを使い、ブロードキャスタへ現在のユーザーには、イベントをブロードキャストしないように指示できます。
Laravel Echoインスタンスを初期化する時、接続へソケットIDをアサインします。VueとAxiosを使用していれば、X-Socket-IDヘッダとして、送信する全リクエストへ自動的に付加されます。そのため、toOthersメソッドを呼び出す場合、LaravelはヘッダからソケットIDを取り除き、そのソケットIDを使い全接続へブロードキャストしないように、ブロードキャスタに対し指示します。
VueとAxiosを使用しない場合、JavaScriptアプリケーションでX-Socket-IDヘッダを送信するように、設定する必要があります。ソケットIDはEcho.socketIdメソッドにより取得できます。
var socketId = Echo.socketId();
Laravel EchoはJavaScriptライブラリで、チャンネルの購読とLaravelによるイベントブロードキャストのリッスンを苦労なしに実現してくれます。EchoはNPMパッケージマネージャにより、インストールします。以降の例で、Pusherブロードキャストを使用する予定のため、pusher-jsパッケージもインストールしています。
npm install --save laravel-echo pusher-js
Echoがインストールできたら、アプリケーションのJavaScriptで、真新しいEchoインスタンスを作成する準備が整いました。これを行うには、Laravelフレームワークに含まれている、resources/assets/js/bootstrap.jsファイルの最後が、良いでしょう。
import Echo from "laravel-echo"
window.Echo = new Echo({
broadcaster: 'pusher',
key: 'your-pusher-key'
});
pusherコネクタを使うEchoインスタンスを作成するときには、clusterと同時に接続の暗号化を行うかどうかを指定することもできます。
window.Echo = new Echo({
broadcaster: 'pusher',
key: 'your-pusher-key',
cluster: 'eu',
encrypted: true
});
インストールが済み、Echoをインスタンス化したら、イベントブロードキャストをリスニングする準備が整いました。最初に、channelメソッドを使い、チャンネルインスタンスを取得し、それからlistenメソッドで特定のイベントをリッスンしてください。
Echo.channel('orders')
.listen('OrderShipped', (e) => {
console.log(e.order.name);
});
プライベートチャンネルのイベントをリッスンしたい場合は、privateメソッドを代わりに使用してください。一つのチャンネルに対し、複数のイベントをリッスンする場合は、listenメソッドをチェーンして呼び出してください。
Echo.private('orders')
.listen(...)
.listen(...)
.listen(...);
チャンネルを離脱するには、Echoインスタンスのleaveメソッドを呼び出してください。
Echo.leave('orders');
上の例で、イベントクラスの完全な名前空間を指定していないことに、皆さん気がついたでしょう。その理由は、EchoはイベントがApp\Events名前空間へ設置されると仮定しているからです。しかし、ルートの名前空間を設定変更している場合は、Echoのインスタンス化時に、namespace設定オプションを渡してください。
window.Echo = new Echo({
broadcaster: 'pusher',
key: 'your-pusher-key',
namespace: 'App.Other.Namespace'
});
もしくは、Echoを使用し購入する時点で、イベントクラスへ.を使い、プリフィックスを付けてください。
Echo.channel('orders')
.listen('.Namespace.Event.Class', (e) => {
//
});
プレゼンスチャンネルは、誰がチャンネルを購入しているかの情報を取得できる機能を提供しつつ、安全なプライベートチャンネルを構築します。これにより、他のユーザーが同じページを閲覧していることを知らせるような、パワフルでコラボレート可能な機能を持つアプリケーションを簡単に構築できます。
全プレゼンスチャンネルは、プライベートチャンネルでもあります。そのため、ユーザーはアクセスする許可が必要です。プレゼンスチャンネルの認可コールバックを定義する場合、ユーザーがチャンネルへ参加する許可があるならば、trueをリターンしないでください。代わりに、ユーザー情報の配列を返してください。
認可コールバックから返されるデータは、JavaScriptアプリケーションのプレゼンスチャンネルイベントリスナで利用できるようになります。ユーザーがプレゼンスチャンネルへ参加する許可がない場合は、falseかnullを返してください。
Broadcast::channel('chat.{roomId}', function ($user, $roomId) {
if ($user->canJoinRoom($roomId)) {
return ['id' => $user->id, 'name' => $user->name];
}
});
プレゼンスチャンネルへ参加するには、Echoのjoinメソッドを使用します。joinメソッドは、既に説明したlistenメソッドに付け加え、here、joining、leavingイベントを購入できるようになっている、PresenceChannel実装を返します。
Echo.join('chat.' + roomId)
.here((users) => {
//
})
.joining((user) => {
console.log(user.name);
})
.leaving((user) => {
console.log(user.name);
});
hereコールバックはチャンネル参加に成功すると、すぐに実行されます。そして、このチャンネルを現在購入している、他の全ユーザー情報を含む配列を返します。joiningメソッドは、チャンネルに新しいユーザーが参加した時に実行されます。一方のleavingメソッドは、ユーザーがチャンネルから離脱した時に実行されます。
プレゼンスチャンネルはパブリックやプライベートチャンネルと同じように、イベントを受け取ります。チャットルームを例にしましょう。その部屋のプレゼンスチャンネルへのNewMessageイベントがブロードキャストされるのを受け取りたいとします。そのために、イベントのbroadcastOnメソッドで、PresenceChannelのインスタンスを返します。
/**
* イベントをブロードキャストすべき、チャンネルの取得
*
* @return Channel|array
*/
public function broadcastOn()
{
return new PresenceChannel('room.'.$this->message->room_id);
}
パブリックやプライベートイベントと同様に、プレゼンスチャンネルイベントはbroadcast関数を使用し、ブロードキャストされます。他のイベントと同様に、ブロードキャストから受けるイベントから、現在のユーザーを除くために、toOthersメソッドも利用できます。
broadcast(new NewMessage($message));
broadcast(new NewMessage($message))->toOthers();
Echoのlistenメソッドにより、参加イベントをリッスンできます。
Echo.join('chat.' + roomId)
.here(...)
.joining(...)
.leaving(...)
.listen('NewMessage', (e) => {
//
});
Laravelアプリケーションに全く関係ないイベントを他の接続クライアントへブロードキャストしたい場合もあるでしょう。これは特にアプリケーションユーザーへ他のユーザーがキーボードをタイプしているメッセージを表示するための「タイプ中」通知のような場合に便利です。クライアントイベントをブロードキャストするには、Echoのwhisperメソッドを使用します。
Echo.channel('chat')
.whisper('typing', {
name: this.user.name
});
クライアントイベントをリッスンするには、listenForWhisperメソッドを使います。
Echo.channel('chat')
.listenForWhisper('typing', (e) => {
console.log(e.name);
});
イベントブロードキャストと通知をペアリングすることで、JavaScriptアプリケーションはページを再読み込みする必要なく、新しい通知を受け取ることができます。最初に、ブロードキャスト通知チャンネルの使用のドキュメントをよく読んでください。
ブロードキャストチャンネルを使用する通知の設定を済ませたら、Echoのnotificationメソッドを使用し、ブロードキャストイベントをリッスンできます。チャンネル名は、通知を受けるエンティティのクラス名と一致している必要があることを覚えておいてください。
Echo.private('App.User.' + userId)
.notification((notification) => {
console.log(notification.type);
});
上記の例の場合、「ブロードキャスト」チャンネルを通じ、App\Userインスタンスへ送られる通知はすべて、コールバックにより受け取られます。App.User.{id}チャンネルのチャンネル認可コールバックは、Laravelフレームワークに用意されている、デフォルトのBroadcastServiceProviderに含まれています。