Laravel 8.x ブロードキャスト

イントロダクション

最近の多くの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などのコミュニティ主導のパッケージは、商業プロバイダを必要としないブロードキャストドライバを提供しています。

Tip!! イベントブロードキャストに飛び込む前に、イベントとリスナに関する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_KEYPUSHER_APP_SECRETPUSHER_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 Websocket

laravel-websocketsパッケージは、PHP製でLaravel用のPusher互換WebSocketパッケージです。このパッケージを使用すると、商用WebSocketプロバイダなしにLaravelブロードキャストの全機能を活用できます。このパッケージのインストールと使用の詳細については、公式ドキュメントを参照してください。

Ably

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をインストールして設定する準備が整いました。

オープンソースの代替

laravel-websocketsパッケージは、PHP製のLaravel用Pusher互換WebSocketパッケージです。このパッケージを使用すると、商用WebSocketプロバイダを使用せずとも、Laravelブロードキャストの全機能を活用できます。このパッケージのインストールと使用の詳細は、公式ドキュメントを参照してください。

クライアント側インストール

Pusherチャンネル

Laravel EchoはJavaScriptライブラリであり、チャンネルをサブスクライブして、サーバ側のブロードキャストドライバがブロードキャストしたイベントを簡単にリッスンできます。NPMパッケージマネージャーを介してEchoをインストールします。以下の例では、Pusherチャンネルブロードキャスタを使用するため、pusher-jsパッケージもインストールしています。

npm install --save-dev laravel-echo pusher-js

Echoをインストールできたら、アプリケーションのJavaScriptで新しいEchoインスタンスを生成する用意が整います。これを実行するのに最適な場所は、Laravelフレームワークに含まれているresources/js/bootstrap.jsファイルの下部です。デフォルトで、Echo設定の例はすでにこのファイルに含まれています。コメントアウトするだけです。

import Echo from 'laravel-echo';

window.Pusher = require('pusher-js');

window.Echo = new Echo({
    broadcaster: 'pusher',
    key: process.env.MIX_PUSHER_APP_KEY,
    cluster: process.env.MIX_PUSHER_APP_CLUSTER,
    forceTLS: true
});

コメントを外し、必要に応じEcho設定を調整したら、アプリケーションのアセットをコンパイルします。

npm run dev

Tip!! アプリケーションのJavaScriptアセットのコンパイルの詳細は、Laravel Mixのドキュメントを参照してください。

既存のクライアントインスタンスの使用

Echoで利用したい事前設定済みのPusherチャンネルクライアントインスタンスがすでにある場合は、client設定オプションによりEchoへ渡せます。

import Echo from 'laravel-echo';

const client = require('pusher-js');

window.Echo = new Echo({
    broadcaster: 'pusher',
    key: 'your-pusher-channels-key',
    client: client
});

Ably

Laravel EchoはJavaScriptライブラリであり、チャンネルをサブスクライブして、サーバ側のブロードキャストドライバがブロードキャストしたイベントを簡単にリッスンできます。NPMパッケージマネージャーを介してEchoをインストールします。この例では、pusher-jsパッケージもインストールしています。

イベントのブロードキャストにAblyを使用しているのに、なぜpusher-jsJavaScriptライブラリをインストールするのか不思議に思うかもしれません。ありがたいことに、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';

window.Pusher = require('pusher-js');

window.Echo = new Echo({
    broadcaster: 'pusher',
    key: process.env.MIX_ABLY_PUBLIC_KEY,
    wsHost: 'realtime-pusher.ably.io',
    wsPort: 443,
    disableStats: true,
    encrypted: true,
});

Ably Echo設定はMIX_ABLY_PUBLIC_KEY環境変数を参照していることに注意してください。この変数の値へAbly公開鍵を指定する必要があります。公開鍵は、:文字の前にあるAbly鍵の部分です。

アンコメントし、必要に応じEcho設定を調整したら、アプリケーションのアセットをコンパイルします。

npm run dev

Tip!! アプリケーションのJavaScriptアセットコンパイルの詳細は、Laravel Mixのドキュメントを参照してください。

概論

Laravelのイベントブロードキャストを使用すると、WebSocketに対するドライバベースのアプローチを使用して、サーバ側のLaravelイベントをクライアント側のJavaScriptアプリケーションへブロードキャストできます。現在、Laravelは[Pusherチャンネル](https://pusher.com/channels]とAblyドライバを用意しています。イベントは、Laravel Echo JavaScriptパッケージを用い、クライアント側で簡単に利用できます。

イベントは「チャンネル」を介してブロードキャストされます。チャンネルは、パブリックまたはプライベートとして指定できます。アプリケーションへの訪問者は全員、認証や認可なしにパブリックチャンネルにサブスクライブできます。ただし、プライベートチャンネルをサブスクライブするには、ユーザーがそのチャンネルでリッスンする認証と認可を持っている必要があります。

Tip!! Pusherの代わりにPHP駆動のオープンソースを使用したい場合は、laravel-websocketsパッケージを調べてください。

サンプルアプリケーションの使用

イベントブロードキャストの各コンポーネントに飛び込む前に、eコマースストアのサンプルを使用して概要を説明しましょう。

このアプリケーションでは、ユーザーが注文の配送ステータスを表示できるページがあると仮定します。また、出荷ステータスの更新がアプリケーションによって処理されるときに、OrderShipmentStatusUpdatedイベントが発生すると仮定します。

use App\Events\OrderShipmentStatusUpdated;

OrderShipmentStatusUpdated::dispatch($order);

ShouldBroadcastインターフェイス

ユーザーが注文の1つを表示しているときに、ステータスの更新を表示するためにページを更新する必要はありません。その代わりに、発生時にアプリケーションへ更新をブロードキャストしたいと思います。したがって、OrderShipmentStatusUpdatedイベントをShouldBroadcastインターフェイスでマークする必要があります。これにより、イベントが発生したときにイベントをブロードキャストするようにLaravelへ指示します。

<?php

namespace App\Events;

use App\Order;
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 OrderShipmentStatusUpdated implements ShouldBroadcast
{
    /**
     * 注文インスタンス
     *
     * @var \App\Order
     */
    public $order;
}

ShouldBroadcastインターフェイスは、イベントに対しbroadcastOnメソッドを定義するよう要求しています。このメソッドは、イベントがブロードキャストする必要があるチャンネルを返す役割を持っています。このメソッドの空のスタブは、生成したイベントクラスですでに定義済みなため、詳細を入力するだけで済みます。注文の作成者だけがステータスの更新を表示できるようにしたいので、その注文に関連付いたプラ​​イベートチャンネルでイベントをブロードキャストします。

/**
 * イベントをブロードキャストするチャンネルを取得
 *
 * @return \Illuminate\Broadcasting\PrivateChannel
 */
public function broadcastOn()
{
    return new PrivateChannel('orders.'.$this->order->id);
}

チャンネルの認可

ユーザーはプライベートチャンネルをリッスンする許可を持っている必要があることを忘れないでください。アプリケーションのroutes/channels.phpファイルでチャンネル認可ルールを定義できます。この例では、プライベートのorder.1チャンネルをリッスンしようとしているユーザーが、実際に注文の作成者であることを確認する必要があります。

use App\Models\Order;

Broadcast::channel('orders.{orderId}', function ($user, $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メソッドは、イベントがブロードキャストする必要があるチャンネルまたはチャンネルの配列を返す必要があります。チャンネルは、ChannelPrivateChannelPresenceChannelのインスタンスである必要があります。Channelインスタンスは、すべてのユーザーがサブスクライブできるパブリックチャンネルを表し、PrivateChannelsPresenceChannelsは、チャンネル認証を必要とするプライベートチャンネルを表します。

<?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;

    /**
     * サーバを作成したユーザー
     *
     * @var \App\Models\User
     */
    public $user;

    /**
     * 新しいイベントインスタンスの生成
     *
     * @param  \App\Models\User  $user
     * @return void
     */
    public function __construct(User $user)
    {
        $this->user = $user;
    }

    /**
     * イベントがブロードキャストするチャンネルを取得
     *
     * @return Channel|array
     */
    public function broadcastOn()
    {
        return new PrivateChannel('user.'.$this->user->id);
    }
}

ShouldBroadcastインターフェイスを実装した後は、通常どおりイベントを発生させるだけです。イベントが発生すると、キュー投入済みのジョブで、指定したブロードキャストドライバを使用し、イベントを自動的にブロードキャストします。

ブロードキャスト名

デフォルトでは、Laravelはイベントのクラス名を使用してイベントをブロードキャストします。ただし、イベントでbroadcastAsメソッドを定義することにより、ブロードキャスト名をカスタマイズできます。

/**
 * イベントのブロードキャスト名
 *
 * @return string
 */
public function broadcastAs()
{
    return 'server.created';
}

broadcastAsメソッドを使用してブロードキャスト名をカスタマイズする場合は、リスナを先頭の.文字で登録する必要があります。これにより、アプリケーションの名前空間をイベントの先頭に追加しないようにEchoに指示します。

.listen('.server.created', function (e) {
    ....
});

ブロードキャストデータ

イベントをブロードキャストすると、そのすべてのpublicプロパティが自動的にシリアライズされ、イベントのペイロードとしてブロードキャストされるため、JavaScriptアプリケーションからそのパブリックデータにアクセスできます。したがって、たとえば、イベントにEloquentモデルを含む単一のパブリック$userプロパティがある場合、イベントのブロードキャストペイロードは次のようになります。

{
    "user": {
        "id": 1,
        "name": "Patrick Stewart"
        ...
    }
}

ただし、ブロードキャストペイロードをよりきめ細かく制御したい場合は、イベントにbroadcastWithメソッドを追加できます。このメソッドは、ブロードキャストするデータの配列をイベントペイロードとして返す必要があります。

/**
 * ブロードキャストするデータを取得
 *
 * @return array
 */
public function broadcastWith()
{
    return ['id' => $this->user->id];
}

ブロードキャストキュー

デフォルトで各ブロードキャストイベントは、queue.php設定ファイルで指定したデフォルトキュー接続のデフォルトキューへ配置されます。イベントクラスでconnectionプロパティとqueueプロパティを定義することにより、ブロードキャスタが使用するキュー接続と名前をカスタマイズできます。

/**
 * イベントをブロードキャストするときに使用するキュー接続の名前
 *
 * @var string
 */
public $connection = 'redis';

/**
 * ブロードキャストジョブを配置するキューの名前
 *
 * @var string
 */
public $queue = 'default';

デフォルトのキュードライバーの代わりにsyncキューを使用してイベントをブロードキャストする場合は、ShouldBroadcastの代わりにShouldBroadcastNowインターフェイスを実装できます。

<?php

use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;

class OrderShipmentStatusUpdated implements ShouldBroadcastNow
{
    //
}

ブロードキャスト条件

特定の条件が真である場合にのみイベントをブロードキャストしたい場合があります。イベントクラスにbroadcastWhenメソッドを追加することで、こうした条件を定義できます。

/**
 * このイベントをブロードキャストするかどうかを判定
 *
 * @return bool
 */
public function broadcastWhen()
{
    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;
}

Tip!! こうした問題の回避方法の詳細は、キュー投入済みジョブとデータベーストランザクションに関するドキュメントを確認してください。

チャンネルの認可

プライベートチャンネルでは現在認証済みのユーザーが、実際にチャンネルをリッスンできることを認可する必要があります。これは、チャンネル名を使用して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(false, response.data);
                })
                .catch(error => {
                    callback(true, error);
                });
            }
        };
    },
})

認可コールバックの定義

次に、現在の認証済みユーザーが特定のチャンネルをリッスンできるかどうかを実際に決定するロジックを定義する必要があります。これは、アプリケーションに含まれているroutes/channels.phpファイルで行われます。このファイルでは、Broadcast::channelメソッドを使用してチャンネル認可コールバックを登録できます。

Broadcast::channel('orders.{orderId}', function ($user, $orderId) {
    return $user->id === Order::findOrNew($orderId)->user_id;
});

channelメソッドは、チャンネルの名前と、ユーザーがチャンネルでのリッスンを許可されているかどうかを示すtrueまたはfalseを返すコールバックの2引数を取ります。

すべての認可コールバックは、現在の認証済みユーザーを最初の引数に取り、追加のワイルドカードパラメーターを後続の引数として受け取ります。この例では、{orderId}プレースホルダーを使用して、チャンネル名の「ID」部分がワイルドカードであることを示しています。

認可コールバックモデルのバインド

HTTPルートと同様に、チャンネルルートも暗黙的および明示的なルートモデルバインディングを利用できます。たとえば、文字列または数値の注文IDを受け取る代わりに、実際のOrderモデルインスタンスを要求できます。

use App\Models\Order;

Broadcast::channel('orders.{order}', function ($user, Order $order) {
    return $user->id === $order->user_id;
});

Note: 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
{
    /**
     * 新しいチャンネルインスタンスの生成
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * チャンネルへのユーザーのアクセスを認可
     *
     * @param  \App\Models\User  $user
     * @param  \App\Models\Order  $order
     * @return array|bool
     */
    public function join(User $user, Order $order)
    {
        return $user->id === $order->user_id;
    }
}

Tip!! Laravelの他の多くのクラスと同様に、チャンネルクラスはサービスコンテナによって自動的に依存解決されます。そのため、コンストラクターでチャンネルに必要な依存関係をタイプヒントすることができます。

ブロードキャストイベント

イベントを定義し、ShouldBroadcastインターフェイスでマークを付けたら、イベントのディスパッチメソッドを使用してイベントを発生させるだけです。イベントディスパッチャは、イベントがShouldBroadcastインターフェイスでマークされていることに気付き、ブロードキャストのためにイベントをキューに入れます。

use App\Events\OrderShipmentStatusUpdated;

OrderShipmentStatusUpdated::dispatch($order);

他の人だけへの送信

イベントブロードキャストを利用するアプリケーションを構築する場合、現在のユーザーを除く特定のチャンネルのすべてのサブスクライバにイベントをブロードキャストする必要がある場合があります。これは、broadcastヘルパとtoOthersメソッドを使用して実行できます。

use App\Events\OrderShipmentStatusUpdated;

broadcast(new OrderShipmentStatusUpdated($update))->toOthers();

toOthersメソッドをいつ使用したらよいかをよりよく理解するために、ユーザーがタスク名を入力して新しいタスクを作成できるタスクリストアプリケーションを想像してみましょう。タスクを作成するために、アプリケーションは、タスクの作成をブロードキャストし、新しいタスクのJSON表現を返す/taskURLにリクエストを送信する場合があります。JavaScriptアプリケーションがエンドポイントから応答を受信すると、次のように新しいタスクをタスクリストに直接挿入する場合があるでしょう。

axios.post('/task', task)
    .then((response) => {
        this.tasks.push(response.data);
    });

ただし、タスクの作成もブロードキャストすることを忘れないでください。JavaScriptアプリケーションがタスクリストにタスクを追加するためにこのイベントもリッスンしている場合、リストには重複するタスクが発生します。1つはエンドポイントからのもので、もう1つはブロードキャストからのものです。これを解決するには、toOthersメソッドを使用して、現在のユーザーにイベントをブロードキャストしないようにブロードキャスターに指示します。

Note: toOthersメソッドを呼び出すには、イベントでIlluminate\Broadcasting\InteractsWithSocketsトレイトを使用する必要があります。

設定

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 メソッドを呼び出して、イベントのブロードキャスト接続を指定することもできます。

<?php

namespace App\Events;

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 OrderShipmentStatusUpdated implements ShouldBroadcast
{
    /**
     * 新イベントインスタンスの生成
     *
     * @return void
     */
    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(...);

チャンネルの離脱

チャンネルを離れるには、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を返す必要があります。

Broadcast::channel('chat.{roomId}', function ($user, $roomId) {
    if ($user->canJoinRoom($roomId)) {
        return ['id' => $user->id, 'name' => $user->name];
    }
});

プレゼンスチャンネルへの接続

プレゼンスチャンネルに参加するには、Echoのjoinメソッドを使用できます。joinメソッドはPresenceChannel実装を返します。これは、listenメソッドを公開するとともに、herejoining、および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 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) => {
        //
    });

モデルブロードキャスト

Note: モデルブロードキャストに関する以降のドキュメントを読む前に、Laravelのモデルブロードキャストサービスの一般的なコンセプトや、ブロードキャストイベントを手動で作成したり、リッスンしたりする方法に精通しておくことをおすすめします。

アプリケーションのEloquentモデルが作成、更新、または削除されたときにイベントをブロードキャストするのは一般的です。もちろん、これは自前でEloquentモデルの状態変化を表すカスタムイベントを定義し、それらのイベントをShouldBroadcastインターフェイスでマークすることで簡単に実現できます。

しかし、こうしたイベントをアプリケーション内で他の目的に使用しない場合、イベントをブロードキャストするためだけにイベントクラスを作成するのは面倒なことです。そのためLaravelは指定があれば、Eloquentモデルの状態変化を自動的にブロードキャストします。

まず始めに、Eloquentモデルで、Illuminate\Database\BroadcastsEventsトレイトを使用する必要があります。さらに、そのモデルでbroadcastsOnメソッドを定義する必要があります。このメソッドは、モデルイベントをブロードキャストするチャンネルの配列を返します。

<?php

namespace App\Models;

use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Database\Eloquent\BroadcastsEvents;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    use BroadcastsEvents, HasFactory;

    /**
     * ポストが属するユーザーの取得
     */
    public function user()
    {
        return $this->belongsTo(User::class);
    }

    /**
     * モデルイベントをブロードキャストするチャンネルを取得
     *
     * @param  string  $event
     * @return \Illuminate\Broadcasting\Channel|array
     */
    public function broadcastOn($event)
    {
        return [$this, $this->user];
    }
}

モデルでこの特性を使い、そしてブロードキャスト・チャンネルを定義したら、モデルインスタンスの作成、更新、削除、ソフトデリートと復元のとき、自動的にイベントのブロードキャストが開始されます。

さらに、broadcastOn メソッドが文字列の$event引数を受け取っていることにお気づきでしょう。この引数には、モデルで発生したイベントの種類が含まれており、createdupdateddeletedtrashedrestoredのいずれかの値を持ちます。この変数の値を調べることで、必要であれば、特定のイベントに対しモデルをどのチャンネルへブロードキャストするかを判定できます。

/**
 * モデルイベントをブロードキャストするチャンネルを取得
 *
 * @param  string  $event
 * @return \Illuminate\Broadcasting\Channel|array
 */
public function broadcastOn($event)
{
    return match($event) {
        'deleted' => [],
        default => [$this, $this->user],
    };
}

モデルブロードキャストのイベント生成のカスタマイズ

時には、モデルのブロードキャスティングイベントの裏で動作している、Laravelの作成方法をカスタマイズしたい場合も起きるでしょう。そのためには、EloquentモデルにnewBroadcastableEventメソッドを定義してください。このメソッドは、Illuminate\Database\Eloquent\BroadcastableModelEventOccurredインスタンスを返す必要があります。

use Illuminate\Database\Eloquent\BroadcastableModelEventOccurred

/**
 * このモデルのための新しいブロードキャストモデルイベントを作成
 *
 * @param  string  $event
 * @return \Illuminate\Database\Eloquent\BroadcastableModelEventOccurred
 */
protected function newBroadcastableEvent($event)
{
    return (new BroadcastableModelEventOccurred(
        $this, $event
    ))->dontBroadcastToCurrentUser();
}

モデルブロードキャスト規約

チャンネル規約

お気づきかもしれませんが、上記のモデル例のbroadcastOnメソッドは、Channelインスタンスを返していません。代わりにEloquentモデルを直接返しています。モデルのbroadcastOnメソッドが、Eloquentモデルインスタンスを返す場合(または、メソッドが返す配列に含まれている場合)、Laravelはモデルのクラス名と主キー識別子をチャンネル名とする、モデルのプライベートチャンネルインスタンスを自動的にインスタンス化します。

つまり、id1App\Models\Userモデルは、App.Models.User.1という名前のIlluminate\Broadcasting\PrivateChannelインスタンスへ変換されるわけです。もちろん、モデルのbroadcastOnメソッドから、Eloquentモデルインスタンスを返すことに加え、モデルのチャンネル名を完全にコントロールするため、完全なChannelインスタンスを返すこともできます。

use Illuminate\Broadcasting\PrivateChannel;

/**
 * モデルイベントをブロードキャストするチャンネルを取得
 *
 * @param  string  $event
 * @return \Illuminate\Broadcasting\Channel|array
 */
public function broadcastOn($event)
{
    return [new PrivateChannel('user.'.$this->id)];
}

モデルのbroadcastOnメソッドからチャンネルのインスタンスを明示的に返す場合は、チャンネルのコンストラクタにEloquentモデルのインスタンスを渡すことができます。そうすると、Laravelは上述のモデルチャンネルの規約を使って、Eloquentモデルをチャンネル名の文字列に変換します。

return [new Channel($this->user)];

モデルのチャンネル名を決定する必要がある場合は、モデルインスタンスでbroadcastChannelメソッドを呼び出してください。たとえば、1idを持つ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という名前のイベントをブロードキャストします。

必要であれば、モデルに broadcastAsbroadcastWith メソッドを追加することで、カスタムのブロードキャスト名とペイロードを定義することができます。これらのメソッドは、発生しているモデルのイベント/操作の名前を受け取るので、モデルの操作ごとにイベントの名前やペイロードをカスタマイズできます。もし、broadcastAsメソッドからnullが返された場合、Laravelはイベントをブロードキャストする際に、上記で説明したモデルのブロードキャストイベント名の規約を使用します。

/**
 * モデルイベントのブロードキャスト名
 *
 * @param  string  $event
 * @return string|null
 */
public function broadcastAs($event)
{
    return match ($event) {
        'created' => 'post.created',
        default => null,
    };
}

/**
 * モデルのブロードキャストのデータ取得
 *
 * @param  string  $event
 * @return array
 */
public function broadcastWith($event)
{
    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);
    });

クライアントイベント

Tip!! 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に含まれています。

ドキュメント章別ページ

ヘッダー項目移動

注目:アイコン:ページ内リンク設置(リンクがないヘッダーへの移動では、リンクがある以前のヘッダーのハッシュをURLへ付加します。

移動

クリックで即時移動します。

設定

適用ボタンクリック後に、全項目まとめて適用されます。

カラーテーマ
和文指定 Pagination
和文指定 Scaffold
Largeスクリーン表示幅
インデント
本文フォント
コードフォント
フォント適用確認

フォントの指定フィールドから、フォーカスが外れると、当ブロックの内容に反映されます。EnglishのDisplayもPreviewしてください。

フォント設定時、表示に不具合が出た場合、当サイトのクッキーを削除してください。

バックスラッシュを含むインライン\Code\Blockの例です。

以下はコードブロックの例です。

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * ユーザに関連する電話レコードを取得
     */
    public function phone()
    {
        return $this->hasOne('App\Phone');
    }
}

設定を保存する前に、表示が乱れないか必ず確認してください。CSSによるフォントファミリー指定の知識がない場合は、フォントを変更しないほうが良いでしょう。

キーボード・ショートカット

オープン操作

PDC

ページ(章)移動の左オフキャンバスオープン

HA

ヘッダー移動モーダルオープン

MS

移動/設定の右オフキャンバスオープン

ヘッダー移動

T

最初のヘッダーへ移動

E

最後のヘッダーへ移動

NJ

次ヘッダー(H2〜H4)へ移動

BK

前ヘッダー(H2〜H4)へ移動

その他

?

このヘルプページ表示
閉じる