イントロダクション
最近の多くのWebアプリケーションでは、WebSocketを使用して、リアルタイムのライブ更新ユーザーインターフェイスを実装しています。サーバ上で一部のデータが更新されると、通常、メッセージはWebSocket接続を介して送信され、クライアントによって処理されます。WebSocketは、UIに反映する必要のあるデータの変更をアプリケーションのサーバから継続的にポーリングするよりも、効率的な代替手段を提供しています。
たとえば、アプリケーションがユーザーのデータをCSVファイルにエクスポートして電子メールで送信できると想像してください。ただ、このCSVファイルの作成には数分かかるため、キュー投入するジョブ内でCSVを作成し、メールで送信することを選択したとしましょう。CSVを作成しユーザーにメール送信したら、イベントブロードキャストを使用して、アプリケーションのJavaScriptで受信するApp\Events\UserDataExported
イベントをディスパッチできます。イベントを受信したら、ページを更新することなく、CSVをメールで送信済みであるメッセージをユーザーへ表示できます。
こうした機能の構築を支援するため、LaravelはWebSocket接続を介してサーバ側のLaravelイベントを簡単に「ブロードキャスト」できます。Laravelイベントをブロードキャストすると、サーバ側のLaravelアプリケーションとクライアント側のJavaScriptアプリケーション間で同じイベント名とデータを共有できます。
ブロードキャストの基本的なコンセプトは簡単で、クライアントはフロントエンドで指定したチャンネルへ接続し、Laravelアプリケーションはバックエンドでこれらのチャンネルに対し、イベントをブロードキャストします。こうしたイベントには、フロントエンドで利用する追加データを含められます。
サポートしているドライバ
Laravelはデフォルトで、PusherチャンネルとAbly、2つのサーバ側ブロードキャストドライバを用意しています。ただし、laravel-websocketsやsoketiなど、コミュニティ主導のパッケージでは、商用ブロードキャストプロバイダを必要としないドライバを提供しています。
Note: イベントブロードキャストに取り掛かる前に、イベントとリスナに関するLaravelのドキュメントをしっかりと読んでください。
サーバ側インストール
Laravelのイベントブロードキャストの使用を開始するには、Laravelアプリケーション内でいくつかの設定を行い、いくつかのパッケージをインストールする必要があります。
イベントブロードキャストは、Laravel Echo(JavaScriptライブラリ)がブラウザクライアント内でイベントを受信できるように、Laravelイベントをブロードキャストするサーバ側ブロードキャストドライバによって実行されます。心配いりません。以降から、インストール手順の各部分を段階的に説明します。
設定
アプリケーションのイベントブロードキャスト設定はすべて、config/broadcasting.php
設定ファイルに保存します。Laravelは、すぐに使用できるブロードキャストドライバをいくつかサポートしています。Pusherチャンネル、Redis、およびローカルでの開発とデバッグ用のlog
ドライバです。さらに、テスト中にブロードキャストを完全に無効にできるnull
ドライバも用意しています。これら各ドライバの設定例は、config/broadcasting.php
設定ファイルにあります。
ブロードキャストサービスプロバイダ
イベントをブロードキャストする前に、まずApp\Providers\BroadcastServiceProvider
を登録する必要があります。新しいLaravelアプリケーションでは、config/app.php
設定ファイルのproviders
配列で、このプロバイダをアンコメントするだけです。このBroadcastServiceProvider
には、ブロードキャスト認可ルートとコールバックを登録するために必要なコードが含まれています。
キュー設定
また、キューワーカを設定して実行する必要があります。すべてのイベントブロードキャストはジョブをキュー投入し実行されるため、アプリケーションの応答時間は、ブロードキャストするイベントによる深刻な影響を受けません。
Pusherチャンネル
Pusherチャンネルを使用してイベントをブロードキャストする場合は、Composerパッケージマネージャを使用してPusher Channels PHP SDKをインストールする必要があります。
composer require pusher/pusher-php-server
次に、config/broadcasting.php
設定ファイルでPusherチャンネルの利用資格情報を設定する必要があります。Pusherチャンネル設定の例は予めこのファイルに含まれているため、キー、シークレット、およびアプリケーションIDを簡単に指定できます。通常、これらの値は、PUSHER_APP_KEY
、PUSHER_APP_SECRET
、PUSHER_APP_ID
環境変数を介して設定する必要があります。
PUSHER_APP_ID=your-pusher-app-id
PUSHER_APP_KEY=your-pusher-key
PUSHER_APP_SECRET=your-pusher-secret
PUSHER_APP_CLUSTER=mt1
config/broadcasting.php
ファイルのpusher
設定では、クラスターなどチャンネルでサポートされている追加のoptions
を指定することもできます。
次に、.env
ファイルでブロードキャストドライバをpusher
に変更する必要があります。
BROADCAST_DRIVER=pusher
これで、クライアント側でブロードキャストイベントを受信するLaravel Echoをインストールして設定する準備が整いました。
オープンソースによるPusherの代替
laravel-websocketsとsoketiパッケージは、Laravel用のPusher互換WebSocketサーバを提供します。これらのパッケージを使用することにより、商用WebSocketプロバイダーを使わずとも、Laravelブロードキャストの機能をフルに活用できます。これらのパッケージのインストールと使用に関する詳細は、オープンソース代替のドキュメントを参照してください。
Ably
Note: 以下のドキュメントでは、Ablyを「Pusher互換」モードで使用する方法について説明していきます。しかし、Ablyチームは、Ablyが提供するユニークな機能を活用できるブロードキャスターとEchoクライアントを推奨し、保守しています。Ablyが保守するドライバの使用に関する詳細については、AblyのLaravelブロードキャスターのドキュメントを参照してください。
Ablyを使用してイベントをブロードキャストする場合は、Composerパッケージマネージャを使用してAbly PHP SDKをインストールする必要があります。
composer require ably/ably-php
次に、config/broadcasting.php
設定ファイルでAblyの接続資格情報を設定する必要があります。Ablyの設定例はすでにこのファイルに用意されているため、キーを手軽に指定できます。通常、この値はABLY_KEY
環境変数により設定する必要があります。
ABLY_KEY=your-ably-key
次に、.env
ファイルでブロードキャストドライバをably
に変更する必要があります。
BROADCAST_DRIVER=ably
これで、クライアント側でブロードキャストイベントを受信するLaravel Echoをインストールして設定する準備が整いました。
オープンソースの代替
PHP
laravel-websocketsパッケージは、PHP製のLaravel用Pusher互換WebSocketパッケージです。このパッケージを使用すると、商用WebSocketプロバイダを使用せずとも、Laravelブロードキャストの全機能を活用できます。このパッケージのインストールと使用の詳細は、公式ドキュメントを参照してください。
Node
Soketiは、NodeベースでPusher互換のLaravel用WebSocketサーバです。Soketiの内部では、µWebSockets.jsを利用し、極めて高いスケーラビリティとスピードを実現しています。このパッケージにより、商用WebSocketプロバイダを使用せずにLaravelブロードキャスティングの能力をフルに活用することができます。このパッケージのインストールと使用に関する詳細は、公式ドキュメントを参照してください。
クライアント側インストール
Pusherチャンネル
Laravel
EchoはJavaScriptライブラリであり、チャンネルをサブスクライブし、サーバ側のブロードキャストドライバがブロードキャストしたイベントを簡単にリッスンできます。NPMパッケージマネージャを介してEchoをインストールします。以下の例では、Pusher
Channelsブロードキャスタを使用するため、pusher-js
パッケージもインストールしています。
npm install --save-dev laravel-echo pusher-js
Echoをインストールできたら、アプリケーションのJavaScriptで新しいEchoインスタンスを生成する用意が整います。これを実行するのに最適な場所は、Laravelフレームワークに含まれているresources/js/bootstrap.js
ファイルの下部です。デフォルトで、Echo設定の例はすでにこのファイルに含まれています。アンコメントするだけです。
import Echo from 'laravel-echo';
import Pusher from 'pusher-js';
window.Pusher = Pusher;
window.Echo = new Echo({
broadcaster: 'pusher',
key: import.meta.env.VITE_PUSHER_APP_KEY,
cluster: import.meta.env.VITE_PUSHER_APP_CLUSTER,
forceTLS: true
});
アンコメントし、必要に応じEcho設定を調整したら、アプリケーションのアセットをコンパイルします。
npm run dev
Note: アプリケーションで使用しているJavaScriptリソースのコンパイルについて詳しく知りたい場合は、Viteドキュメントを参照してください。
既存のクライアントインスタンスの使用
Echoで利用したい事前設定済みのPusherチャンネルクライアントインスタンスがすでにある場合は、client
設定オプションによりEchoへ渡せます。
import Echo from 'laravel-echo';
import Pusher from 'pusher-js';
const options = {
broadcaster: 'pusher',
key: 'your-pusher-channels-key'
}
window.Echo = new Echo({
...options,
client: new Pusher(options.key, options)
});
Ably
Note: 以下のドキュメントでは、Ablyを「Pusher互換」モードで使用する方法について説明していきます。しかし、Ablyチームは、Ablyが提供するユニークな機能を活用できるブロードキャスターとEchoクライアントを推奨し、保守しています。Ablyが保守するドライバの使用に関する詳細については、AblyのLaravelブロードキャスターのドキュメントを参照してください。
Laravel
EchoはJavaScriptライブラリであり、チャンネルをサブスクライブして、サーバ側のブロードキャストドライバがブロードキャストしたイベントを簡単にリッスンできます。NPMパッケージマネージャを介してEchoをインストールします。この例では、pusher-js
パッケージもインストールしています。
イベントのブロードキャストにAblyを使用しているのに、なぜpusher-js
JavaScriptライブラリをインストールするのか不思議に思うかもしれません。ありがたいことに、AblyにはPusher互換モードが含まれており、クライアント側アプリケーションでイベントをリッスンするときにPusherプロトコルを使用できます。
npm install --save-dev laravel-echo pusher-js
続行する前に、Ablyアプリケーション設定でPusherプロトコルサポートを有効にする必要があります。この機能は、Ablyアプリケーションの設定ダッシュボードの「プロトコルアダプター設定」部分で有効にできます。
Echoをインストールしたら、アプリケーションのJavaScriptで新しいEchoインスタンスを生成する準備が整います。これを実行するのに最適な場所は、Laravelフレームワークに含まれているresources/js/bootstrap.js
ファイルの下部です。デフォルトでは、Echo設定の例はすでにこのファイルに用意してあります。ただし、bootstrap.js
ファイルのデフォルト設定はPusherを対象としています。以下の設定をコピーして、設定をAbly向きに変更できます。
import Echo from 'laravel-echo';
import Pusher from 'pusher-js';
window.Pusher = Pusher;
window.Echo = new Echo({
broadcaster: 'pusher',
key: import.meta.env.VITE_ABLY_PUBLIC_KEY,
wsHost: 'realtime-pusher.ably.io',
wsPort: 443,
disableStats: true,
encrypted: true,
});
Ably Echo設定はVITE_ABLY_PUBLIC_KEY
環境変数を参照していることに注意してください。この変数の値へAbly公開鍵を指定する必要があります。公開鍵は、:
文字の前にあるAbly鍵の部分です。
アンコメントし、必要に応じEcho設定を調整したら、アプリケーションのアセットをコンパイルします。
npm run dev
Note: アプリケーションで使用しているJavaScriptリソースのコンパイルについて詳しく知りたい場合は、Viteドキュメントを参照してください。
概論
Laravelのイベントブロードキャストを使用すると、WebSocketに対するドライバベースのアプローチを使用して、サーバ側のLaravelイベントをクライアント側のJavaScriptアプリケーションへブロードキャストできます。現在、LaravelはPusherチャンネルとAblyドライバを用意しています。イベントは、Laravel Echo JavaScriptパッケージを用い、クライアント側で簡単に利用できます。
イベントは「チャンネル」を介してブロードキャストされます。チャンネルは、パブリックまたはプライベートとして指定できます。アプリケーションへの訪問者は全員、認証や認可なしにパブリックチャンネルをサブスクライブできます。ただし、プライベートチャンネルをサブスクライブするには、ユーザーが認証され、そのチャンネルをリッスンする認可を持っている必要があります。
Note: Pusherの代わりにオープンソースを使いたい場合は、オープンソースの代替を読んでください。
サンプルアプリケーションの使用
イベントブロードキャストの各コンポーネントに飛び込む前に、eコマースストアのサンプルを使用して概要を説明しましょう。
このアプリケーションでは、ユーザーが注文の配送ステータスを表示できるページがあると仮定します。また、出荷ステータスの更新がアプリケーションによって処理されるときに、OrderShipmentStatusUpdated
イベントが発生すると仮定します。
use App\Events\OrderShipmentStatusUpdated;
OrderShipmentStatusUpdated::dispatch($order);
ShouldBroadcast
インターフェイス
ユーザーが注文の1つを表示しているときに、ステータスの更新を表示するためにページを更新する必要はありません。その代わりに、発生時にアプリケーションへ更新をブロードキャストしたいと思います。したがって、OrderShipmentStatusUpdated
イベントをShouldBroadcast
インターフェイスでマークする必要があります。これにより、イベントが発生したときにイベントをブロードキャストするようにLaravelへ指示します。
<?php
namespace App\Events;
use App\Models\Order;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Queue\SerializesModels;
class OrderShipmentStatusUpdated implements ShouldBroadcast
{
/**
* 注文インスタンス
*
* @var \App\Order
*/
public $order;
}
ShouldBroadcast
インターフェイスは、イベントに対しbroadcastOn
メソッドを定義するよう要求します。このメソッドは、イベントがブロードキャストする必要があるチャンネルを返す役割を持っています。このメソッドの空のスタブは、生成したイベントクラスですでに定義済みなため、詳細を入力するだけで済みます。注文の作成者だけがステータスの更新を表示できるようにしたいので、その注文に関連付いたプライベートチャンネルでイベントをブロードキャストします。
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\PrivateChannel;
/**
* イベントをブロードキャストするチャンネルを取得
*/
public function broadcastOn(): Channel
{
return new PrivateChannel('orders.'.$this->order->id);
}
If you wish the event to broadcast on multiple channels, you may
return an array
instead:
use Illuminate\Broadcasting\PrivateChannel;
/**
* イベントをブロードキャストするチャンネルを取得
*
* @return array<int, \Illuminate\Broadcasting\Channel>
*/
public function broadcastOn(): array
{
return [
new PrivateChannel('orders.'.$this->order->id),
// ...
];
}
チャンネルの認可
プライベートチャンネルをリッスンするには、ユーザーが認可されていなければならないことを忘れないでください。チャンネルの認可ルールは、アプリケーションのroutes/channels.php
ファイルで定義できます。この例では、orders.1
というプライベートチャンネルをリッスンするユーザーが、実際にそのオーダーの作成者であることを確認しています。
use App\Models\Order;
use App\Models\User;
Broadcast::channel('orders.{orderId}', function (User $user, int $orderId) {
return $user->id === Order::findOrNew($orderId)->user_id;
});
channel
メソッドは、チャンネルの名前と、ユーザーがチャンネルでのリッスンを許可されているかどうかを示すtrue
またはfalse
を返すコールバックの2つの引数を取ります。
すべての認可コールバックは、現在認証されているユーザーを最初の引数として受け取り、追加のワイルドカードパラメーターを後続の引数として受け取ります。この例では、{orderId}
プレースホルダーを使用して、チャンネル名の「ID」部分がワイルドカードであることを示しています。
イベントブロードキャストのリッスン
他に残っているのは、JavaScriptアプリケーションでイベントをリッスンすることだけです。Laravel
Echoを使用してこれを行えます。まず、private
メソッドを使用し、プライベートチャンネルをサブスクライブします。次に、listen
メソッドを使用してOrderShipmentStatusUpdated
イベントをリッスンします。デフォルトでは、イベントのすべてのパブリックプロパティがブロードキャストイベントに含まれます。
Echo.private(`orders.${orderId}`)
.listen('OrderShipmentStatusUpdated', (e) => {
console.log(e.order);
});
ブロードキャストイベントの定義
特定のイベントをブロードキャストする必要があることをLaravelに通知するには、イベントクラスにIlluminate\Contracts\Broadcasting\ShouldBroadcast
インターフェイスを実装する必要があります。このインターフェイスは、フレームワークで生成したすべてのイベントクラスに、最初からインポートされているため、どのイベントでも簡単に追加できます。
ShouldBroadcast
インターフェイスでは、broadcastOn
という単一のメソッドを実装する必要があります。broadcastOn
メソッドは、イベントがブロードキャストする必要があるチャンネルまたはチャンネルの配列を返す必要があります。チャンネルは、Channel
、PrivateChannel
、PresenceChannel
のインスタンスである必要があります。Channel
インスタンスは、すべてのユーザーがサブスクライブできるパブリックチャンネルを表し、PrivateChannels
とPresenceChannels
は、チャンネル認証を必要とするプライベートチャンネルを表します。
<?php
namespace App\Events;
use App\Models\User;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Queue\SerializesModels;
class ServerCreated implements ShouldBroadcast
{
use SerializesModels;
/**
* 新しいイベントインスタンスの生成
*/
public function __construct(
public User $user,
) {}
/**
* イベントがブロードキャストするチャンネルを取得
*
* @return array<int, \Illuminate\Broadcasting\Channel>
*/
public function broadcastOn(): array
{
return [
new PrivateChannel('user.'.$this->user->id),
];
}
}
ShouldBroadcast
インターフェイスを実装した後は、通常どおりイベントを発生させるだけです。イベントが発生すると、キューへジョブへ投入し、指定したブロードキャストドライバを使用し、イベントを自動的にブロードキャストします。
ブロードキャスト名
デフォルトでは、Laravelはイベントのクラス名を使用してイベントをブロードキャストします。ただし、イベントでbroadcastAs
メソッドを定義することにより、ブロードキャスト名をカスタマイズできます。
/**
* イベントのブロードキャスト名
*/
public function broadcastAs(): string
{
return 'server.created';
}
broadcastAs
メソッドを使用してブロードキャスト名をカスタマイズする場合は、リスナを先頭の.
文字で登録する必要があります。これにより、アプリケーションの名前空間をイベントの先頭に追加しないようにEchoに指示します。
.listen('.server.created', function (e) {
....
});
ブロードキャストデータ
イベントをブロードキャストすると、そのすべてのpublic
プロパティが自動的にシリアライズされ、イベントのペイロードとしてブロードキャストされるため、JavaScriptアプリケーションからそのパブリックデータにアクセスできます。したがって、たとえば、イベントにEloquentモデルを含む単一のパブリック$user
プロパティがある場合、イベントのブロードキャストペイロードは次のようになります。
{
"user": {
"id": 1,
"name": "Patrick Stewart"
...
}
}
ただし、ブロードキャストペイロードをよりきめ細かく制御したい場合は、イベントにbroadcastWith
メソッドを追加できます。このメソッドは、ブロードキャストするデータの配列をイベントペイロードとして返す必要があります。
/**
* ブロードキャストするデータを取得
*
* @return array<string, mixed>
*/
public function broadcastWith(): array
{
return ['id' => $this->user->id];
}
ブロードキャストキュー
デフォルトで各ブロードキャストイベントは、queue.php
設定ファイルで指定したデフォルトキュー接続のデフォルトキューへ配置されます。イベントクラスでconnection
プロパティとqueue
プロパティを定義することにより、ブロードキャスタが使用するキュー接続と名前をカスタマイズできます。
/**
* イベントをブロードキャストするときに使用するキュー接続の名前
*
* @var string
*/
public $connection = 'redis';
/**
* ブロードキャストジョブを投入するキュー名
*
* @var string
*/
public $queue = 'default';
あるいは、イベントにbroadcastQueue
メソッドを定義し、キュー名をカスタマイズできます。
/**
* ブロードキャストジョブを投入するキュー名
*/
public function broadcastQueue(): string
{
return 'default';
}
デフォルトのキュードライバではなく、sync
キューを使用してイベントをブロードキャストしたい場合は、ShouldBroadcast
の代わりにShouldBroadcastNow
インターフェイスを実装します。
<?php
use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;
class OrderShipmentStatusUpdated implements ShouldBroadcastNow
{
// ...
}
ブロードキャスト条件
特定の条件が真である場合にのみイベントをブロードキャストしたい場合があります。イベントクラスにbroadcastWhen
メソッドを追加することで、こうした条件を定義できます。
/**
* このイベントをブロードキャストするかどうかを判定
*/
public function broadcastWhen(): bool
{
return $this->order->value > 100;
}
ブロードキャストとデータベーストランザクション
ブロードキャストイベントがデータベーストランザクション内でディスパッチされると、データベーストランザクションがコミットされる前にキューによって処理される場合があります。これが起きると、データベーストランザクション中にモデルまたはデータベースレコードに加えた更新は、データベースにまだ反映されていない可能性があります。さらに、トランザクション内で作成されたモデルまたはデータベースレコードは、データベースに存在しない可能性があります。イベントがこれらのモデルに依存している場合、イベントをブロードキャストするジョブの処理時に予期しないエラーが発生する可能性があります。
キュー接続のafter_commit
設定オプションがfalse
に設定されている場合でも、イベントクラスで$afterCommit
プロパティを定義することにより、開いているすべてのデータベーストランザクションがコミットされた後に特定のブロードキャストイベントをディスパッチする必要があることを示すことができます。
<?php
namespace App\Events;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Queue\SerializesModels;
class ServerCreated implements ShouldBroadcast
{
use SerializesModels;
public $afterCommit = true;
}
Note: こうした問題の回避方法の詳細は、キュー投入済みジョブとデータベーストランザクションに関するドキュメントを確認してください。
チャンネルの認可
プライベートチャンネルでは現在認証済みのユーザーが、実際にチャンネルをリッスンできることを認可する必要があります。これは、チャンネル名を使用してLaravelアプリケーションにHTTPリクエストを送信し、ユーザーがそのチャンネルでリッスンできるかどうかをアプリケーションが判断できるようにすることで実現します。Laravel Echoを使用すると、プライベートチャンネルへのサブスクリプションを認可するためのHTTPリクエストが自動的に行われます。ただし、これらのリクエストに応答するには、適切なルートを定義する必要があります。
認可ルートの定義
便利なことに、Laravelではチャンネル認可リクエストに応答するルートを簡単に定義できます。Laravelアプリケーションに含まれているApp\Providers\BroadcastServiceProvider
で、Broadcast::routes
メソッドの呼び出しが見つかるでしょう。このメソッドは、認可リクエストを処理するために/Broadcasting/auth
ルートを登録します。
Broadcast::routes();
Broadcast::routes
メソッドは自動的にそのルートをweb
ミドルウェアグループ内に配置します。ただし、割り当てられた属性をカスタマイズする場合は、ルート属性の配列をメソッドに渡してください。
Broadcast::routes($attributes);
認可エンドポイントのカスタマイズ
デフォルトでEchoは/Broadcasting/auth
エンドポイントを使用してチャンネルアクセスを認可します。ただし、authEndpoint
設定オプションをEchoインスタンスに渡すことで、独自の認可エンドポイントを指定できます。
window.Echo = new Echo({
broadcaster: 'pusher',
// ...
authEndpoint: '/custom/endpoint/auth'
});
認可リクエストのカスタマイズ
Laravel Echoの初期化時に、カスタムAuthorizerを指定し、Laravel Echoの認可リクエスト実行方法をカスタマイズできます。
window.Echo = new Echo({
// ...
authorizer: (channel, options) => {
return {
authorize: (socketId, callback) => {
axios.post('/api/broadcasting/auth', {
socket_id: socketId,
channel_name: channel.name
})
.then(response => {
callback(null, response.data);
})
.catch(error => {
callback(error);
});
}
};
},
})
認可コールバックの定義
次に、現在の認証済みユーザーが特定のチャンネルをリッスンできるかどうかを実際に決定するロジックを定義する必要があります。これは、アプリケーションに含まれているroutes/channels.php
ファイルで行います。このファイルでBroadcast::channel
メソッドを使用し、チャンネル認可コールバックを登録します。
use App\Models\User;
Broadcast::channel('orders.{orderId}', function (User $user, int $orderId) {
return $user->id === Order::findOrNew($orderId)->user_id;
});
channel
メソッドは、チャンネルの名前と、ユーザーがチャンネルでのリッスンを許可されているかどうかを示すtrue
またはfalse
を返すコールバックの2つの引数を取ります。
すべての認可コールバックは、現在認証されているユーザーを最初の引数として受け取り、追加のワイルドカードパラメーターを後続の引数として受け取ります。この例では、{orderId}
プレースホルダーを使用して、チャンネル名の「ID」部分がワイルドカードであることを示しています。
channel:list
Artisanコマンドを使用すると、アプリケーションのブロードキャスト認可コールバックのリストを表示できます。
php artisan channel:list
認可コールバックモデルのバインド
HTTPルートと同様に、チャンネルルートも暗黙的および明示的なルートモデルバインディングを利用できます。たとえば、文字列または数値の注文IDを受け取る代わりに、実際のOrder
モデルインスタンスを要求できます。
use App\Models\Order;
use App\Models\User;
Broadcast::channel('orders.{order}', function (User $user, Order $order) {
return $user->id === $order->user_id;
});
Warning!! HTTPルートモデルバインディングとは異なり、チャンネルモデルバインディングは自動暗黙的モデルバインディングスコープをサポートしていません。ただし、ほとんどのチャンネルは単一のモデルの一意の主キーに基づいてスコープを設定できるため、これが問題になることはめったにありません。
認可コールバック認証
プライベートおよびプレゼンスブロードキャストチャンネルは、アプリケーションのデフォルトの認証ガードを介して現在のユーザーを認証します。ユーザーが認証されていない場合、チャンネル認可は自動的に拒否され、認可コールバックは実行されません。ただし、必要に応じて、受信リクエストを認証する複数の必要なカスタムガードを割り当てることができます。
Broadcast::channel('channel', function () {
// ...
}, ['guards' => ['web', 'admin']]);
チャンネルクラスの定義
アプリケーションが多くの異なるチャンネルを使用している場合、routes/channels.php
ファイルがかさばる可能性が起きます。そのため、クロージャを使用してチャンネルを認可する代わりに、チャンネルクラスを使用できます。チャンネルクラスを生成するには、make:channel
Artisanコマンドを使用します。このコマンドは、新しいチャンネルクラスをApp/Broadcasting
ディレクトリに配置します。
php artisan make:channel OrderChannel
次に、チャンネルをroutes/channels.php
ファイルに登録します。
use App\Broadcasting\OrderChannel;
Broadcast::channel('orders.{order}', OrderChannel::class);
最後に、チャンネルの認可ロジックをチャンネルクラスのjoin
メソッドに配置できます。このjoin
メソッドは、チャンネル認可クロージャに通常配置するのと同じロジックを格納します。チャンネルモデルバインディングを利用することもできます。
<?php
namespace App\Broadcasting;
use App\Models\Order;
use App\Models\User;
class OrderChannel
{
/**
* 新しいチャンネルインスタンスの生成
*/
public function __construct()
{
// ...
}
/**
* チャンネルへのユーザーのアクセスを認可
*/
public function join(User $user, Order $order): array|bool
{
return $user->id === $order->user_id;
}
}
Note: Laravelの他の多くのクラスと同様に、チャンネルクラスはサービスコンテナによって自動的に依存解決されます。そのため、コンストラクターでチャンネルに必要な依存関係をタイプヒントすることができます。
ブロードキャストイベント
イベントを定義し、ShouldBroadcast
インターフェイスでマークを付けたら、イベントのディスパッチメソッドを使用してイベントを発生させるだけです。イベントディスパッチャは、イベントがShouldBroadcast
インターフェイスでマークされていることに気付き、ブロードキャストのためにイベントをキューに入れます。
use App\Events\OrderShipmentStatusUpdated;
OrderShipmentStatusUpdated::dispatch($order);
他の人だけへの送信
イベントブロードキャストを利用するアプリケーションを構築する場合、特定のチャンネルで現在のユーザーを除く、すべてのサブスクライバにイベントをブロードキャストする必要が起きる場合があります。これは、broadcast
ヘルパとtoOthers
メソッドを使用して実行できます。
use App\Events\OrderShipmentStatusUpdated;
broadcast(new OrderShipmentStatusUpdated($update))->toOthers();
toOthers
メソッドをいつ使用したらよいかをよりよく理解するために、ユーザーがタスク名を入力して新しいタスクを作成できるタスクリストアプリケーションを想像してみましょう。タスクを作成するために、アプリケーションは、タスクの作成をブロードキャストし、新しいタスクのJSON表現を返す/task
URLにリクエストを送信する場合があります。JavaScriptアプリケーションがエンドポイントから応答を受信すると、次のように新しいタスクをタスクリストに直接挿入する場合があるでしょう。
axios.post('/task', task)
.then((response) => {
this.tasks.push(response.data);
});
ただし、タスクの作成もブロードキャストすることを忘れないでください。JavaScriptアプリケーションがタスクリストにタスクを追加するためにこのイベントもリッスンしている場合、リストには重複するタスクが発生します。1つはエンドポイントからのもので、もう1つはブロードキャストからのものです。これを解決するには、toOthers
メソッドを使用して、現在のユーザーにはイベントをブロードキャストしないようにブロードキャスタへ指示します。
Warning!!
toOthers
メソッドを呼び出すには、イベントでIlluminate\Broadcasting\InteractsWithSockets
トレイトをuseする必要があります。
設定
Laravel
Echoインスタンスを初期化すると、ソケットIDが接続に割り当てられます。グローバルAxiosインスタンスを使用してJavaScriptアプリケーションからHTTPリクエストを作成している場合、ソケットIDはすべての送信リクエストにX-Socket-ID
ヘッダとして自動的に添付されます。次に、toOthers
メソッドを呼び出すと、LaravelはヘッダからソケットIDを抽出し、そのソケットIDを持つ接続にブロードキャストしないようにブロードキャスタに指示します。
グローバルAxiosインスタンスを使用しない場合は、すべての送信リクエストでX-Socket-ID
ヘッダを送信するようにJavaScriptアプリケーションを手作業で設定する必要があります。Echo.socketId
メソッドを使用してソケットIDを取得できます。
var socketId = Echo.socketId();
コネクションのカスタマイズ
アプリケーションが複数のブロードキャスト接続とやりとりしており、デフォルト以外のブロードキャスタを使いイベントをブロードキャストしたい場合は、via
メソッドを使ってどの接続へイベントをプッシュするか指定できます。
use App\Events\OrderShipmentStatusUpdated;
broadcast(new OrderShipmentStatusUpdated($update))->via('pusher');
もしくは、イベントのコンストラクタで broadcastVia
メソッドを呼び出して、イベントのブロードキャスト接続を指定することもできます。ただし、そのときは、イベントクラスで確実にInteractsWithBroadcasting
トレイトをuseしてください。
<?php
namespace App\Events;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithBroadcasting;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Queue\SerializesModels;
class OrderShipmentStatusUpdated implements ShouldBroadcast
{
use InteractsWithBroadcasting;
/**
* 新しいイベントインスタンスの生成
*/
public function __construct()
{
$this->broadcastVia('pusher');
}
}
ブロードキャストの受け取り
イベントのリッスン
Laravel
Echoをインストールしてインスタンス化すると、Laravelアプリケーションからブロードキャストされるイベントをリッスンする準備が整います。まず、channel
メソッドを使用してチャンネルのインスタンスを取得し、次にlisten
メソッドを呼び出して指定されたイベントをリッスンします。
Echo.channel(`orders.${this.order.id}`)
.listen('OrderShipmentStatusUpdated', (e) => {
console.log(e.order.name);
});
プライベートチャンネルでイベントをリッスンする場合は、代わりにprivate
メソッドを使用してください。listen
メソッドへの呼び出しをチェーンして、単一のチャンネルで複数のイベントをリッスンすることができます。
Echo.private(`orders.${this.order.id}`)
.listen(/* ... */)
.listen(/* ... */)
.listen(/* ... */);
イベントリッスンの中止
チャンネルから離れることなく特定のイベントのリッスンを停止したい場合は、stopListening
メソッドを使用します。
Echo.private(`orders.${this.order.id}`)
.stopListening('OrderShipmentStatusUpdated')
チャンネルの離脱
チャンネルを離れるには、EchoインスタンスでleaveChannel
メソッドを呼び出してください。
Echo.leaveChannel(`orders.${this.order.id}`);
チャンネルとそれに関連するプライベートチャンネルおよびプレゼンスチャンネルを離れたい場合は、leave
メソッドを呼び出してください。
Echo.leave(`orders.${this.order.id}`);
名前空間
上記の例で、イベントクラスに完全なApp\Events
名前空間を指定していないことに気付いたかもしれません。これは、EchoがイベントがApp\Events
名前空間にあると自動的に想定するためです。ただし、namespace
設定オプションを渡すことにより、Echoをインスタンス化するときにルート名前空間を設定できます。
window.Echo = new Echo({
broadcaster: 'pusher',
// ...
namespace: 'App.Other.Namespace'
});
または、Echoを使用してサブスクライブするときに、イベントクラスの前に.
を付けることもできます。これにより、常に完全修飾クラス名を指定できます。
Echo.channel('orders')
.listen('.Namespace\\Event\\Class', (e) => {
// ...
});
プレゼンスチャンネル
プレゼンスチャンネルは、プライベートチャンネルのセキュリティを基盤とし、チャンネルにサブスクライブしているユーザーを認識するという追加機能を付け加えます。これにより、別のユーザーが同じページを表示しているときにユーザーに通知したり、チャットルームの住民を一覧表示したりするなど、強力なコラボレーションアプリケーション機能を簡単に構築できます。
プレゼンスチャンネルの認可
すべてのプレゼンスチャンネルもプライベートチャンネルです。したがって、ユーザーはアクセス許可を持つ必要があります。ただし、プレゼンスチャンネルの認可コールバックを定義する場合、ユーザーがチャンネルへの参加を認可されている場合に、true
を返しません。代わりに、ユーザーに関するデータの配列を返す必要があります。
認可コールバックが返すデータは、JavaScriptアプリケーションのプレゼンスチャンネルイベントリスナが利用できるようになります。ユーザーがプレゼンスチャンネルへの参加を許可されていない場合は、false
またはnull
を返す必要があります。
use App\Models\User;
Broadcast::channel('chat.{roomId}', function (User $user, int $roomId) {
if ($user->canJoinRoom($roomId)) {
return ['id' => $user->id, 'name' => $user->name];
}
});
プレゼンスチャンネルへの接続
プレゼンスチャンネルに参加するには、Echoのjoin
メソッドを使用できます。join
メソッドはPresenceChannel
実装を返します。これは、listen
メソッドを公開するとともに、here
、joining
、およびleaving
イベントをサブスクライブできるようにします。
Echo.join(`chat.${roomId}`)
.here((users) => {
// ...
})
.joining((user) => {
console.log(user.name);
})
.leaving((user) => {
console.log(user.name);
})
.error((error) => {
console.error(error);
});
here
コールバックは、チャンネルへ正常に参加するとすぐに実行され、現在チャンネルにサブスクライブしている他のすべてのユーザーのユーザー情報を含む配列を受け取ります。joining
メソッドは、新しいユーザーがチャンネルに参加したときに実行され、leaving
メソッドは、ユーザーがチャンネルを離れたときに実行されます。error
メソッドは、認証エンドポイントが200以外のHTTPステータスコードを返した場合や、返されたJSONの解析で問題があった場合に実行されます。
プレゼンスチャンネルへのブロードキャスト
プレゼンスチャンネルは、パブリックチャンネルまたはプライベートチャンネルと同じようにイベントを受信できます。チャットルームの例を使用して、NewMessage
イベントをルームのプレゼンスチャンネルにブロードキャストしたいとしましょう。そのために、イベントのbroadcastOn
メソッドからPresenceChannel
のインスタンスを返します。
/**
* イベントをブロードキャストするチャンネルを取得
*
* @return array<int, \Illuminate\Broadcasting\Channel>
*/
public function broadcastOn(): array
{
return [
new PresenceChannel('chat.'.$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) => {
// ...
});
モデルブロードキャスト
Warning!! モデルブロードキャストに関する以降のドキュメントを読む前に、Laravelのモデルブロードキャストサービスの一般的なコンセプトや、ブロードキャストイベントを手作業で作成したり、リッスンしたりする方法に精通しておくことをおすすめします。
アプリケーションのEloquentモデルが作成、更新、または削除されたときにイベントをブロードキャストするのは一般的です。もちろん、これは自前でEloquentモデルの状態変化を表すカスタムイベントを定義し、それらのイベントをShouldBroadcast
インターフェイスでマークすることで簡単に実現できます。
しかし、こうしたイベントをアプリケーション内で他の目的に使用しない場合、イベントをブロードキャストするためだけにイベントクラスを作成するのは面倒なことです。そのためLaravelは指定があれば、Eloquentモデルの状態変化を自動的にブロードキャストします。
まず始めに、Eloquentモデルで、Illuminate\Database\BroadcastsEvents
トレイトを使用する必要があります。さらに、そのモデルでbroadcastOn
メソッドを定義する必要があります。このメソッドは、モデルイベントをブロードキャストするチャンネルの配列を返します。
<?php
namespace App\Models;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Database\Eloquent\BroadcastsEvents;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class Post extends Model
{
use BroadcastsEvents, HasFactory;
/**
* ポストが属するユーザーの取得
*/
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
/**
* モデルイベントをブロードキャストするチャンネルを取得
*
* @return array<int, \Illuminate\Broadcasting\Channel|\Illuminate\Database\Eloquent\Model>
*/
public function broadcastOn(string $event): array
{
return [$this, $this->user];
}
}
モデルでこの特性を使い、そしてブロードキャスト・チャンネルを定義したら、モデルインスタンスの作成、更新、削除、ソフトデリートと復元のとき、自動的にイベントのブロードキャストが開始されます。
さらに、broadcastOn
メソッドが文字列の$event
引数を受け取っていることにお気づきでしょう。この引数には、モデルで発生したイベントの種類が含まれており、created
、updated
、deleted
、trashed
、restored
のいずれかの値を持ちます。この変数の値を調べることで、必要であれば、特定のイベントに対しモデルをどのチャンネルへブロードキャストするかを判定できます。
/**
* モデルイベントをブロードキャストするチャンネルを取得
*
* @return array<string, array<int, \Illuminate\Broadcasting\Channel|\Illuminate\Database\Eloquent\Model>>
*/
public function broadcastOn(string $event): array
{
return match ($event) {
'deleted' => [],
default => [$this, $this->user],
};
}
モデルブロードキャストのイベント生成のカスタマイズ
時には、Laravelのモデルブロードキャスティングイベントの作成方法をカスタマイズしたい場合も起きるでしょう。それには、EloquentモデルにnewBroadcastableEvent
メソッドを定義してください。このメソッドは、Illuminate\Database\Eloquent\BroadcastableModelEventOccurred
インスタンスを返す必要があります。
use Illuminate\Database\Eloquent\BroadcastableModelEventOccurred;
/**
* このモデルのための新しいブロードキャストモデルイベントを作成
*/
protected function newBroadcastableEvent(string $event): BroadcastableModelEventOccurred
{
return (new BroadcastableModelEventOccurred(
$this, $event
))->dontBroadcastToCurrentUser();
}
モデルブロードキャスト規約
チャンネル規約
お気づきかもしれませんが、上記のモデル例のbroadcastOn
メソッドは、Channel
インスタンスを返していません。代わりにEloquentモデルを直接返しています。モデルのbroadcastOn
メソッドが、Eloquentモデルインスタンスを返す場合(または、メソッドが返す配列に含まれている場合)、Laravelはモデルのクラス名と主キー識別子をチャンネル名とする、モデルのプライベートチャンネルインスタンスを自動的にインスタンス化します。
つまり、id
が1
のApp\Models\User
モデルは、App.Models.User.1
という名前のIlluminate\Broadcasting\PrivateChannel
インスタンスへ変換されるわけです。もちろん、モデルのbroadcastOn
メソッドから、Eloquentモデルインスタンスを返すことに加え、モデルのチャンネル名を完全にコントロールするため、完全なChannel
インスタンスを返すこともできます。
use Illuminate\Broadcasting\PrivateChannel;
/**
* モデルイベントをブロードキャストするチャンネルを取得
*
* @return array<int, \Illuminate\Broadcasting\Channel>
*/
public function broadcastOn(string $event): array
{
return [
new PrivateChannel('user.'.$this->id)
];
}
モデルのbroadcastOn
メソッドからチャンネルのインスタンスを明示的に返す場合は、チャンネルのコンストラクタにEloquentモデルのインスタンスを渡すことができます。そうすると、Laravelは上述のモデルチャンネルの規約を使って、Eloquentモデルをチャンネル名の文字列に変換します。
return [new Channel($this->user)];
モデルのチャンネル名を決定する必要がある場合は、モデルインスタンスでbroadcastChannel
メソッドを呼び出してください。たとえば、1
のid
を持つApp\Models\User
モデルに対し、このメソッドは文字列App.Models.User.1
を返します。
$user->broadcastChannel()
イベント規約
モデルのブロードキャストイベントは、アプリケーションのApp\Events
ディレクトリ内の「実際の」イベントとは関連していないので、規約に基づいて名前とペイロードが割り当てられます。Laravelの規約では、モデルのクラス名(名前空間を含まない)と、ブロードキャストのきっかけとなったモデルイベントの名前を使って、イベントをブロードキャストします。
ですから、例えば、App\Models\Post
モデルの更新は、以下のペイロードを持つPostUpdated
として、クライアントサイドのアプリケーションにイベントをブロードキャストします。
{
"model": {
"id": 1,
"title": "My first post"
...
},
...
"socket": "someSocketId",
}
App\Models\User
モデルが削除されると、UserDeleted
という名前のイベントをブロードキャストします。
必要であれば、モデルに broadcastAs
と
broadcastWith
メソッドを追加することで、カスタムのブロードキャスト名とペイロードを定義することができます。これらのメソッドは、発生しているモデルのイベント/操作の名前を受け取るので、モデルの操作ごとにイベントの名前やペイロードをカスタマイズできます。もし、broadcastAs
メソッドからnull
が返された場合、Laravelはイベントをブロードキャストする際に、上記で説明したモデルのブロードキャストイベント名の規約を使用します。
/**
* モデルイベントのブロードキャスト名
*/
public function broadcastAs(string $event): string|null
{
return match ($event) {
'created' => 'post.created',
default => null,
};
}
/**
* モデルのブロードキャストのデータ取得
*
* @return array<string, mixed>
*/
public function broadcastWith(string $event): array
{
return match ($event) {
'created' => ['title' => $this->title],
default => ['model' => $this],
};
}
モデルブロードキャストのリッスン
モデルへBroadcastsEvents
トレイトを追加し、モデルのbroadcastOn
メソッドを定義したら、クライアントサイドのアプリケーションで、ブロードキャストしたモデルイベントをリッスンする準備ができました。始める前に、イベントのリッスンの完全なドキュメントを参照しておくとよいでしょう。
まず、private
メソッドでチャンネルのインスタンスを取得し、それからlisten
メソッドを呼び出して、指定したイベントをリッスンします。通常、private
メソッドへ指定するチャンネル名は、Laravelのモデルブロードキャスト規約に対応していなければなりません。
チャンネルインスタンスを取得したら、listen
メソッドを使って特定のイベントをリッスンします。モデルのブロードキャストイベントは、アプリケーションのApp\Events
ディレクトリにある「実際の」イベントと関連付けられていないため、イベント名の前に.
を付けて、特定の名前空間に属していないことを示す必要があります。各モデルブロードキャストイベントは、そのモデルのブロードキャスト可能なプロパティをすべて含むmodel
プロパティを持ちます。
Echo.private(`App.Models.User.${this.user.id}`)
.listen('.PostUpdated', (e) => {
console.log(e.model);
});
クライアントイベント
Note: Pusherチャンネルを使用する場合は、クライアントイベントを送信するためにアプリケーションダッシュボードの"App Settings"セクションの"Client Events"オプションを有効にする必要があります。
Laravelアプリケーションにまったくアクセスせずに、接続済みの他のクライアントにイベントをブロードキャストしたい場合があります。これは、別のユーザーが特定の画面でメッセージを入力していることをアプリケーションのユーザーに警告する「入力」通知などに特に役立ちます。
クライアントイベントをブロードキャストするには、Echoのwhisper
メソッドを使用できます。
Echo.private(`chat.${roomId}`)
.whisper('typing', {
name: this.user.name
});
クライアントイベントをリッスンするには、listenForWhisper
メソッドを使用します。
Echo.private(`chat.${roomId}`)
.listenForWhisper('typing', (e) => {
console.log(e.name);
});
通知
イベントブロードキャストを通知と組み合わせることで、JavaScriptアプリケーションは、ページを更新せず発生した新しい通知を受け取ることができます。実現する前に、ブロードキャスト通知チャンネルの使用に関するドキュメントを必ずお読みください。
ブロードキャストチャンネルを使用するように通知を設定すると、Echoのnotification
メソッドを使用してブロードキャストイベントをリッスンできます。チャンネル名は、通知を受信するエンティティのクラス名と一致する必要があることに注意してください。
Echo.private(`App.Models.User.${userId}`)
.notification((notification) => {
console.log(notification.type);
});
この例では、broadcast
チャンネルを介してApp\Models\User
インスタンスに送信されたすべての通知は、コールバックにより受け取られます。App.Models.User.{id}
チャンネルのチャンネル認可コールバックは、Laravelフレームワークに付属するデフォルトのBroadcastServiceProvider
に含まれています。