- はじめに
- サーバーサイドのインストール
- クライアントサイドのインストール
- 概念の概要
- ブロードキャストイベントの定義
- チャネルの認可
- イベントのブロードキャスト
- ブロードキャストの受信
- プレゼンスチャネル
- モデルのブロードキャスト
- クライアントイベント
- 通知
#はじめに
多くのモダンなウェブアプリケーションでは、WebSocketを使ってリアルタイムで更新されるユーザーインターフェースを実装しています。サーバー上でデータが更新されると、通常はWebSocket接続を通じてクライアントにメッセージが送信されます。WebSocketは、UIに反映すべきデータの変更を継続的にサーバーにポーリングするよりも効率的な方法を提供します。
例えば、アプリケーションがユーザーのデータをCSVファイルにエクスポートしてメール送信できるとします。しかし、CSVファイルの作成には数分かかるため、キュージョブ内でCSVの作成とメール送信を行うことにします。CSVが作成されてユーザーにメール送信されたら、App\Events\UserDataExported イベントをブロードキャストしてJavaScript側で受信します。イベントを受信したら、ユーザーにページをリロードせずにCSVがメール送信されたことを通知できます。
こうした機能を簡単に構築できるように、LaravelはサーバーサイドのLaravel イベントをWebSocket接続で「ブロードキャスト」する仕組みを提供しています。Laravelイベントをブロードキャストすることで、サーバーサイドのLaravelアプリケーションとクライアントサイドのJavaScriptアプリケーションで同じイベント名とデータを共有できます。
ブロードキャストの基本的な考え方はシンプルです。クライアントはフロントエンドで名前付きチャネルに接続し、Laravelアプリケーションはバックエンドでこれらのチャネルにイベントをブロードキャストします。イベントにはフロントエンドに渡したい任意の追加データを含められます。
#対応ドライバー
Laravelにはデフォルトで3つのサーバーサイドブロードキャストドライバーが用意されています:Laravel Reverb、Pusher Channels、Ablyです。
イベントブロードキャストを始める前に、Laravelのイベントとリスナーのドキュメントを必ずお読みください。
#サーバーサイドのインストール
Laravelのイベントブロードキャストを使い始めるには、Laravelアプリケーション内で設定を行い、いくつかのパッケージをインストールする必要があります。
イベントブロードキャストはサーバーサイドのブロードキャストドライバーによって実現されます。これによりLaravelイベントがブロードキャストされ、JavaScriptライブラリのLaravel Echoがブラウザクライアントで受信できます。インストール手順は順を追って説明しますのでご安心ください。
#設定
アプリケーションのイベントブロードキャスト設定はすべて config/broadcasting.php に保存されます。Laravelは標準でいくつかのブロードキャストドライバーをサポートしています:Pusher Channels、Redis、ローカル開発やデバッグ用の log ドライバーです。さらに、テスト時にブロードキャストを完全に無効化できる null ドライバーも含まれています。これらのドライバーの設定例はすべて config/broadcasting.php にあります。
#ブロードキャストサービスプロバイダ
イベントをブロードキャストする前に、まず App\Providers\BroadcastServiceProvider を登録する必要があります。新しいLaravelアプリケーションでは、config/app.php の providers 配列内でこのプロバイダーのコメントアウトを外すだけで済みます。この BroadcastServiceProvider はブロードキャストの認可ルートとコールバックを登録するためのコードを含みます。
#キューの設定
イベントブロードキャストはすべてキュージョブ経由で行われるため、キューワーカーを設定して実行する必要があります。これによりイベントのブロードキャストがアプリケーションの応答時間に大きな影響を与えません。
#Reverb
Composerパッケージマネージャーを使ってReverbをインストールできます:
composer require laravel/reverb
パッケージをインストールしたら、Reverbのインストールコマンドを実行して設定ファイルを公開し、アプリケーションのブロードキャスト設定を更新し、Reverbに必要な環境変数を追加します:
php artisan reverb:install
詳しいReverbのインストールと使い方はReverbのドキュメントをご覧ください。
#Pusher Channels
Pusher Channelsを使ってイベントをブロードキャストする場合は、ComposerパッケージマネージャーでPusher ChannelsのPHP SDKをインストールしてください:
composer require pusher/pusher-php-server
次に、config/broadcasting.php でPusher Channelsの認証情報を設定します。このファイルにはPusher Channelsの設定例が含まれており、キー、シークレット、アプリケーション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 設定では、クラスタなどChannelsがサポートする追加の options も指定できます。
次に、.env ファイルでブロードキャストドライバーを pusher に変更してください:
BROADCAST_DRIVER=pusher
最後に、クライアント側でブロードキャストイベントを受信するために Laravel Echo をインストールして設定します。
#オープンソースのPusher代替
soketi はLaravel向けのPusher互換WebSocketサーバーを提供し、商用WebSocketプロバイダーなしでLaravelブロードキャストの全機能を活用できます。オープンソースのブロードキャストパッケージのインストールと使用については、オープンソースの代替のドキュメントをご覧ください。
#Ably
以下のドキュメントは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 をインストールして設定します。
#オープンソースの代替
#Node
Soketi はNodeベースのPusher互換WebSocketサーバーで、Laravel向けです。内部ではµWebSockets.jsを使い、極めて高いスケーラビリティと速度を実現しています。このパッケージを使えば商用WebSocketプロバイダーなしでLaravelブロードキャストの全機能を活用できます。インストールと使用方法は公式ドキュメントをご覧ください。
#クライアントサイドのインストール
#Reverb
Laravel Echo はサーバーサイドのブロードキャストドライバーが送信するイベントを購読しやすくするJavaScriptライブラリです。NPMパッケージマネージャーでEchoをインストールできます。この例では、ReverbがPusherプロトコルを使ってWebSocketの購読、チャネル、メッセージを扱うため、pusher-js パッケージもインストールします:
npm install --save-dev laravel-echo pusher-js
Echoをインストールしたら、アプリケーションのJavaScriptで新しいEchoインスタンスを作成します。Laravelフレームワークに含まれる resources/js/bootstrap.js ファイルの末尾が適切な場所です。デフォルトでこのファイルにはEchoの設定例が含まれているので、コメントアウトを外し、broadcaster 設定を reverb に変更するだけで使えます:
import Echo from 'laravel-echo';
import Pusher from 'pusher-js';
window.Pusher = Pusher;
window.Echo = new Echo({
broadcaster: 'reverb',
key: import.meta.env.VITE_REVERB_APP_KEY,
wsHost: import.meta.env.VITE_REVERB_HOST,
wsPort: import.meta.env.VITE_REVERB_PORT,
wssPort: import.meta.env.VITE_REVERB_PORT,
forceTLS: (import.meta.env.VITE_REVERB_SCHEME ?? 'https') === 'https',
enabledTransports: ['ws', 'wss'],
});
次に、アプリケーションのアセットをコンパイルしてください:
npm run build
Laravel Echoの reverb ブロードキャスターはlaravel-echo v1.16.0以上が必要です。
#Pusher Channels
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 build
アプリケーションのJavaScriptアセットのコンパイル方法については、Viteのドキュメントをご覧ください。
#既存のクライアントインスタンスの利用
すでに設定済みのPusher ChannelsクライアントインスタンスをEchoに使わせたい場合は、client 設定オプションで渡せます:
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
以下のドキュメントは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 アプリケーションの設定ダッシュボード内の「Protocol Adapter Settings」セクションで有効化できます。
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
アプリケーションの JavaScript アセットのコンパイル方法については、Vite のドキュメントをご覧ください。
#概要
Laravel のイベントブロードキャストは、ドライバー方式の WebSocket を使ってサーバーサイドの Laravel イベントをクライアントサイドの JavaScript アプリケーションに配信できます。現在、Laravel には Pusher Channels と Ably のドライバーが同梱されています。イベントは Laravel Echo JavaScript パッケージを使って簡単にクライアント側で受信できます。
イベントは「チャネル」を通じてブロードキャストされ、チャネルはパブリックまたはプライベートに指定できます。パブリックチャネルは認証や認可なしで誰でも購読できますが、プライベートチャネルを購読するにはユーザーが認証され、そのチャネルの購読権限を持っている必要があります。
Pusher のオープンソース代替を試したい場合は、オープンソース代替をご覧ください。
#例となるアプリケーションの利用
イベントブロードキャストの各コンポーネントに入る前に、例として e コマースストアを使って全体像を見てみましょう。
このアプリケーションでは、ユーザーが注文の配送状況を確認できるページがあるとします。また、配送状況の更新が処理されると OrderShipmentStatusUpdated イベントが発火すると仮定します。
use App\Events\OrderShipmentStatusUpdated;
OrderShipmentStatusUpdated::dispatch($order);
#ShouldBroadcast インターフェイス
ユーザーが自分の注文を見ているときに、ページをリロードしなくてもステータス更新を確認できるようにしたいです。そのため、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\Models\Order
*/
public $order;
}
ShouldBroadcast インターフェイスは broadcastOn メソッドの定義を要求します。このメソッドはイベントがブロードキャストされるチャネルを返します。生成されたイベントクラスには空のスタブがあるので、詳細を記述するだけで済みます。注文の作成者だけがステータス更新を見られるように、注文に紐づくプライベートチャネルでイベントをブロードキャストします。
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\PrivateChannel;
/**
* イベントがブロードキャストされるチャネルを取得
*/
public function broadcastOn(): Channel
{
return new PrivateChannel('orders.'.$this->order->id);
}
複数のチャネルでイベントをブロードキャストしたい場合は、array を返すこともできます。
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つの引数を受け取ります。
すべての認可コールバックは、最初の引数に現在認証されているユーザーを受け取り、続く引数にワイルドカードパラメータを受け取ります。この例では、チャネル名の「ID」部分がワイルドカードであることを {orderId} プレースホルダーで示しています。
#イベントブロードキャストのリスニング
次に、JavaScript アプリケーションでイベントをリスニングします。これは Laravel Echo を使って行います。まず private メソッドでプライベートチャネルを購読し、listen メソッドで OrderShipmentStatusUpdated イベントをリスニングします。デフォルトでイベントのすべての public プロパティがブロードキャストイベントに含まれます。
Echo.private(`orders.${orderId}`)
.listen('OrderShipmentStatusUpdated', (e) => {
console.log(e.order);
});
#ブロードキャストイベントの定義
Laravel にイベントをブロードキャストさせるには、イベントクラスで Illuminate\Contracts\Broadcasting\ShouldBroadcast インターフェイスを実装する必要があります。このインターフェイスはフレームワークが生成するすべてのイベントクラスにすでにインポートされているため、簡単に追加できます。
ShouldBroadcast インターフェイスでは、1つのメソッドである 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 アプリケーションから public データにアクセスできます。例えば、イベントに Eloquent モデルを含む単一の public $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 に設定されている場合でも、イベントクラスで ShouldDispatchAfterCommit インターフェイスを実装することで、すべての未完了のデータベーストランザクションがコミットされた後に特定のブロードキャストイベントをディスパッチすることを示せます。
<?php
namespace App\Events;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Contracts\Events\ShouldDispatchAfterCommit;
use Illuminate\Queue\SerializesModels;
class ServerCreated implements ShouldBroadcast, ShouldDispatchAfterCommit
{
use SerializesModels;
}
これらの問題を回避する方法について詳しくは、キュージョブとデータベーストランザクション のドキュメントをご覧ください。
#チャンネルの認可
プライベートチャンネルでは、現在認証されているユーザーが実際にそのチャンネルをリッスンできるかどうかを認可する必要があります。これは、チャンネル名を含むHTTPリクエストをLaravelアプリケーションに送信し、ユーザーがそのチャンネルをリッスンできるかどうかを判定することで実現します。Laravel Echo を使用する場合、プライベートチャンネルの購読を認可するためのHTTPリクエストは自動的に行われますが、これらのリクエストに応答する適切なルートを定義する必要があります。
#認可ルートの定義
幸いなことに、Laravelではチャンネル認可リクエストに応答するルートを簡単に定義できます。Laravelアプリケーションに含まれる App\Providers\BroadcastServiceProvider では、Broadcast::routes メソッドの呼び出しが見られます。このメソッドは認可リクエストを処理するために /broadcasting/auth ルートを登録します。
Broadcast::routes();
Broadcast::routes メソッドは自動的にルートを web ミドルウェアグループ内に配置しますが、割り当てる属性をカスタマイズしたい場合は、ルート属性の配列をメソッドに渡せます。
Broadcast::routes($attributes);
#認可エンドポイントのカスタマイズ
デフォルトでは、Echoはチャンネルアクセスの認可に /broadcasting/auth エンドポイントを使用します。ただし、Echoインスタンスに authEndpoint 設定オプションを渡すことで、独自の認可エンドポイントを指定できます。
window.Echo = new Echo({
broadcaster: 'pusher',
// ...
authEndpoint: '/custom/endpoint/auth' // カスタム認可エンドポイント
});
#認可リクエストのカスタマイズ
Laravel Echoが認可リクエストをどのように実行するかは、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 メソッドは2つの引数を受け取ります。1つ目はチャンネル名、2つ目はユーザーがそのチャンネルをリッスンできるかどうかを true または false で返すコールバックです。
すべての認可コールバックは、最初の引数に現在認証されているユーザーを受け取り、続く引数にワイルドカードパラメータを受け取ります。この例では、チャンネル名の「ID」部分がワイルドカードであることを示すために {orderId} プレースホルダーを使用しています。
アプリケーションのブロードキャスト認可コールバックの一覧は、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;
});
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;
}
}
Laravelの多くのクラスと同様に、チャンネルクラスはサービスコンテナによって自動的に解決されます。そのため、コンストラクタで必要な依存関係を型宣言できます。
#イベントのブロードキャスト
イベントを定義し、ShouldBroadcast インターフェイスでマークしたら、イベントを発行するにはイベントの dispatch メソッドを使うだけです。イベントディスパッチャはイベントが ShouldBroadcast インターフェイスでマークされていることを検出し、ブロードキャスト用にイベントをキューに入します:
use App\Events\OrderShipmentStatusUpdated;
OrderShipmentStatusUpdated::dispatch($order);
#自分以外にのみ送信
イベントブロードキャストを利用するアプリケーションでは、時折、現在のユーザーを除くチャンネルのすべての購読者にイベントを送信したい場合があります。これは broadcast ヘルパーと toOthers メソッドを使って実現できます。
use App\Events\OrderShipmentStatusUpdated;
broadcast(new OrderShipmentStatusUpdated($update))->toOthers();
toOthers メソッドの利用シーンを理解するために、タスクリストアプリケーションを想像してみましょう。ユーザーがタスク名を入力して新しいタスクを作成するとします。タスク作成のために /task URL にリクエストを送り、タスクの作成をブロードキャストし、新しいタスクのJSON表現を返すかもしれません。JavaScriptアプリケーションはエンドポイントからのレスポンスを受け取ると、次のように直接タスクリストに新しいタスクを挿入するでしょう。
axios.post('/task', task)
.then((response) => {
this.tasks.push(response.data);
});
しかし、タスクの作成はブロードキャストもしています。JavaScriptアプリケーションがこのイベントをリッスンしてタスクリストに追加している場合、エンドポイントからのタスクとブロードキャストからのタスクが重複してしまいます。toOthers メソッドを使うことで、現在のユーザーにはイベントをブロードキャストしないよう指示でき、この問題を解決できます。
toOthers メソッドを呼び出すには、イベントで Illuminate\Broadcasting\InteractsWithSockets トレイトを使用している必要があります。
#設定
Laravel Echoインスタンスを初期化すると、接続にソケットIDが割り当てられます。JavaScriptアプリケーションでグローバルな Axios インスタンスを使ってHTTPリクエストを送る場合、ソケットIDは自動的にすべての送信リクエストに X-Socket-ID ヘッダーとして付与されます。toOthers メソッドを呼ぶと、LaravelはヘッダーからソケットIDを抽出し、そのソケットIDを持つ接続にはブロードキャストしないよう指示します。
グローバルAxiosインスタンスを使っていない場合は、JavaScriptアプリケーションで送信リクエストに X-Socket-ID ヘッダーを手動で設定する必要があります。ソケットIDは Echo.socketId メソッドで取得できます。
var socketId = Echo.socketId();
#接続のカスタマイズ
アプリケーションが複数のブロードキャスト接続を利用していて、デフォルト以外のブロードキャスターでイベントをブロードキャストしたい場合は、via メソッドでどの接続にイベントを送るか指定できます。
use App\Events\OrderShipmentStatusUpdated;
broadcast(new OrderShipmentStatusUpdated($update))->via('pusher');
または、イベントのコンストラクタ内で broadcastVia メソッドを呼び出してイベントのブロードキャスト接続を指定できます。ただし、その前にイベントクラスが InteractsWithBroadcasting トレイトを使用していることを確認してください。
<?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 メソッドを使います。1つのチャンネルで複数のイベントをリッスンしたい場合は、listen メソッドをチェーンして続けられます。
Echo.private(`orders.${this.order.id}`)
.listen(/* ... */) // イベント1をリッスン
.listen(/* ... */) // イベント2をリッスン
.listen(/* ... */); // イベント3をリッスン
#イベントのリッスン停止
チャンネルを離脱することなく特定のイベントのリッスンだけを停止したい場合は、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 名前空間にあると想定するためです。ただし、Echo をインスタンス化する際に namespace 設定オプションを渡すことで、ルート名前空間を設定できます。
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) => {
// ...
});
#モデルのブロードキャスト
以下のモデルブロードキャストに関するドキュメントを読む前に、Laravel のモデルブロードキャストサービスの基本概念や、手動でブロードキャストイベントを作成・リッスンする方法に慣れておくことをおすすめします。
アプリケーションの Eloquentモデル が作成、更新、削除された際にイベントをブロードキャストするのは一般的です。もちろん、これは手動で Eloquentモデルの状態変化に対するカスタムイベントを定義 し、それらのイベントに ShouldBroadcast インターフェイスを付けることで簡単に実現できます。
しかし、これらのイベントを他の目的で使わない場合、ブロードキャストのためだけにイベントクラスを作成するのは面倒です。そこで Laravel では、Eloquentモデルが状態変化を自動的にブロードキャストするよう指定できます。
始めるには、Eloquentモデルで Illuminate\Database\Eloquent\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 メソッドを呼べます。例えば、id が 1 の 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);
});
#クライアントイベント
Pusher Channels を使用する場合、クライアントイベントを送信するには、アプリケーションダッシュボード の「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 に含まれています。