← ユースケース

Reverb を運用しない Laravel Echo

.env の値を変えるだけ。イベント、チャンネル、whisper、toOthers() はそのまま動きます。

Laravel Broadcasting はもともと Pusher プロトコルを話すので、Laravel アプリは組み込みの pusher 接続のまま Hibira にブロードキャストを送れます。イベント、チャンネルルート、Echo のコードは変わりません。なくなるのは、本来自分で動かすはずだった WebSocket サーバーです。常駐する Reverb プロセス、2 台以上に広げるための Redis、スティッキーなロードバランサー、そして夜中にそれを見守る人。

ユーザーにとっての価値

  • 注文状況、通知、ダッシュボードがリロードなしで更新されます。
  • Echo の join と whisper で、「入力中…」表示とオンライン一覧つきのチャット。
  • ページを開いた瞬間から最新の状態。次のイベントまで空のままにはなりません。

使う機能

  • pusher 接続と pusher/pusher-php-server を使う Laravel Broadcasting。
  • ブラウザ側は Laravel Echo と pusher-js。
  • 自分の routes/channels.php で認可するプライベートチャンネルとプレゼンスチャンネル。
  • クライアントイベント(Echo の whisper)と送信者の除外(toOthers())。
  • 最後のイベントを新しい購読者全員に渡すキャッシュチャンネル(cache-)。

ステップ 1:Broadcasting の向き先を Hibira に

Laravel 11 以降では broadcasting をインストールし、聞かれたら Pusher を選びます。

php artisan install:broadcasting
composer require pusher/pusher-php-server
npm install --save-dev laravel-echo pusher-js

続いて .env に接続を設定します。値はアプリのページにあります。

BROADCAST_CONNECTION=pusher
PUSHER_APP_ID=YOUR_APP_ID
PUSHER_APP_KEY=YOUR_APP_KEY
PUSHER_APP_SECRET=YOUR_APP_SECRET
PUSHER_HOST=ws.hibira.io
PUSHER_PORT=443
PUSHER_SCHEME=https
PUSHER_APP_CLUSTER=ap

VITE_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
VITE_PUSHER_HOST="${PUSHER_HOST}"
VITE_PUSHER_PORT="${PUSHER_PORT}"
VITE_PUSHER_SCHEME="${PUSHER_SCHEME}"

Laravel 10 以前では BROADCAST_CONNECTION の代わりに BROADCAST_DRIVER=pusher を使います。標準の config/broadcasting.php はホスト、ポート、スキームを Pusher ライブラリに渡します。手書きの設定ファイルなら、options がそれらを読んでいるか確認してください。そうすれば pusher.com に接続しにいくことはありません。

ステップ 2:Echo を設定する

// resources/js/echo.js
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: 'ap',
  wsHost: import.meta.env.VITE_PUSHER_HOST,
  wsPort: import.meta.env.VITE_PUSHER_PORT,
  wssPort: import.meta.env.VITE_PUSHER_PORT,
  forceTLS: import.meta.env.VITE_PUSHER_SCHEME === 'https',
  enabledTransports: ['ws', 'wss'],
});

ステップ 3:ひとりの顧客にイベントを配信する

class OrderShipped implements ShouldBroadcast
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public function __construct(public Order $order) {}

    public function broadcastOn(): array
    {
        return [new PrivateChannel('orders.'.$this->order->id)];
    }
}

// routes/channels.php
Broadcast::channel('orders.{id}', fn ($user, $id) => $user->orders()->whereKey($id)->exists());

// Wherever the order ships
OrderShipped::dispatch($order);
Echo.private(`orders.${orderId}`)
  .listen('OrderShipped', (e) => showStatus(e.order));

ShouldBroadcast のイベントはキューを通るので、ワーカー(php artisan queue:work)が動いている必要があります。キューのないプロジェクトでは ShouldBroadcastNow を使えば、リクエスト中に配信されます。

ステップ 4:プレゼンス、whisper、toOthers()

Echo.join(`room.${roomId}`)
  .here((users) => online.set(users))
  .joining((user) => online.add(user))
  .leaving((user) => online.remove(user))
  .listen('MessageSent', (e) => messages.append(e.message))
  .listenForWhisper('typing', (e) => typing.show(e.name, 3000));
broadcast(new MessageSent($message))->toOthers();

whisper はクライアントイベントです。アプリ設定で Allow client events をオンにしてください。toOthers() は X-Socket-ID ヘッダーで送信者自身のタブを除外します。Echo は Axios のリクエストにこのヘッダーを自動で付けますが、fetch を使う場合は 'X-Socket-ID': Echo.socketId() を自分で送ってください。ルーム全体の作り方はチャットのユースケースにあります。

ステップ 5:新しいページにも最新の状態を

名前が cache- で始まるチャンネルに配信すると、あとから購読した人にも最後のイベントがすぐ届きます(30 分間保持)。

public function broadcastOn(): array
{
    return [new Channel('cache-scoreboard')];
}
Echo.channel('cache-scoreboard')
  .listen('ScoreUpdated', (e) => board.render(e.scores));

Echo.private('cache-orders.1') も同じように動き、private-cache-orders.1 チャンネルになります。

Reverb やほかの Pusher 互換サービスからの移行

変わるのは .env の値だけです。イベント、チャンネルルート、Echo のコードはそのままなので、環境ごとに切り替えられ、戻すのも同じくらい簡単です。確認項目は移行ページに、サインイン後は Laravel ガイドにアプリの値を埋めた同じ設定があります。

ヒント

  • 環境(ローカル、ステージング、本番)ごとにアプリを分ければ、テストのイベントが本物のユーザーに届くことはありません。
  • プレゼンスチャンネルのメンバーは最大 100 人です。もっと大きなルームは大規模な視聴者を参照してください。
  • ブロードキャストは何台のブラウザが受け取っても 1 メッセージとして数えます。配信とプレゼンスの更新は無料です。