> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# パッケージのブロードキャストチャンネル認可と上書き

> パッケージがプライベートチャンネルを登録するときの認可の評価順、利用アプリによる上書きの仕組み、HTTPテストでの確認方法を解説します。

パッケージがリアルタイムイベントを配信するときは、**チャンネル名の定義**と**利用者を通す判定**を分けて設計します。パッケージの `boot()` で登録したチャンネルは、利用アプリの `routes/channels.php` と同じ名前だと上書きされることがあります。この登録順と評価順を理解しておくと、保守時の不具合を減らせます。

このページは、Laravel公式ドキュメントの[ブロードキャスト](https://kawax.biz/jp/broadcasting.md)と、確認に使った最新リリース `v13.42.0` の実装を参照しています。イベントの送信方法は[ブロードキャスト](https://kawax.biz/jp/broadcasting.md)、プロバイダーの読み込みは[パッケージ自動検出](https://kawax.biz/jp/advanced/package-discovery.md)を参照してください。

## チャンネル認可の仕組み

プライベートチャンネルを購読すると、クライアントは `/broadcasting/auth` にリクエストを送ります。`BroadcastController::authenticate()` は `Broadcast::auth()` に処理を渡し、`Broadcaster` が登録済みのコールバックを順番に評価します。

評価の規則は次のとおりです。

* コールバックが `false` を返すと、その時点で `AccessDeniedHttpException` が投げられます。
* `true` などの真の値を返すと、その時点で認可が成立します。
* `null` などの偽の値を返すと、次に登録されたコールバックへ進みます。どれも通さなければ、最後に `AccessDeniedHttpException` が投げられます。

> [!NOTE]
>
> `Pusher` と `Reverb` のドライバーは、認可の前に `private-` や `presence-` の接頭辞を取り除きます。そのため、登録するチャンネル名には接頭辞を含めません。

## パッケージでチャンネルを登録する

例として、架空の `Acme\Orders` パッケージで注文の状態変化を配信します。チャンネル名は `acme.orders.{order}` とし、注文の所有者だけが購読できるようにします。

パッケージの `ServiceProvider` では、`boot()` の中で `Broadcast::channel()` を呼び出します。判定はパッケージ内のクラスに委譲します。

```php theme={null}
use Acme\Orders\Broadcasting\OrderChannel;
use Illuminate\Support\Facades\Broadcast;

public function boot(): void
{
    Broadcast::channel('acme.orders.{order}', OrderChannel::class);
}
```

```php theme={null}
<?php

namespace Acme\Orders\Broadcasting;

use Acme\Orders\Models\Order;
use Illuminate\Contracts\Auth\Authenticatable;

class OrderChannel
{
    public function join(Authenticatable $user, Order $order): bool
    {
        return $user->getAuthIdentifier() === $order->owner_id;
    }
}
```

`join()` の第1引数には、認可時のユーザーが渡されます。以降の引数は、チャンネル名の `{...}` の値です。`{order}` のパラメーター名と `Order $order` の型が一致し、型が `UrlRoutable` のサブクラスであれば、ルートモデルのバインディングが働きます。

> [!NOTE]
>
> `Broadcast::channel()` の `$callback` には、`callable` か `join()` メソッドを持つクラス名を指定できます。クラス名の場合は、`Broadcaster::normalizeChannelHandlerToCallable()` がコンテナで生成してから `join()` を呼びます。

## 利用アプリによる上書き

利用アプリの `withBroadcasting()` に渡したファイル（通常は `routes/channels.php`）は、`withBroadcasting()` が登録する `booted` コールバックの中で読み込まれます。`booted` コールバックは、すべてのサービスプロバイダーの `boot()` が終わったあとに実行されるため、パッケージの `boot()` の登録は先に行われます。

| 順序 | 処理 | 登録されるもの |
| - | - | - |
| 1 | パッケージの `boot()` | `acme.orders.{order}` のコールバック |
| 2 | アプリの `booted` でのルート読み込み | `routes/channels.php` のコールバック |

`Broadcaster::channel()` は、チャンネル名をキーにして内部の配列に保存します。そのため、利用アプリが同じ名前を登録すると、2番目の登録がパッケージの登録を置き換えます。Laravelは同名の登録を重複としてエラーにしません。

上書きを防ぐには、チャンネル名を一意にします。例えば `acme.orders.{order}` のように、パッケージ固有の接頭辞を付けると、利用アプリが同じ名前を登録する可能性が下がります。
チャンネル名はクライアントとの公開された契約です。一度決めたら変更しないでください。変更すると、古いクライアントが購読できなくなります。

> [!WARNING]
>
> 同名のチャンネルは、後から登録したものが黙って優先されます。`php artisan channel:list` では、チャンネル名と解決先（クラス名または `Closure`）が表示されるため、どの登録が残っているかを確認できます。

## チャンネルのオプションとガード

`Broadcast::channel()` の第3引数 `$options` は、チャンネルごとの設定として保存されます。`guards` を指定すると、認証に使うガードを順に試します。

```php theme={null}
Broadcast::channel('acme.orders.{order}', OrderChannel::class, [
    'guards' => ['web', 'api'],
]);
```

`guards` を省略すると、リクエストの既定のユーザー（`$request->user()`）を認可に使います。`guards` を指定すると、先頭から順に `$request->user($guard)` を呼び、最初に得られたユーザーを使います。パッケージ側でガード名を固定せず、設定値として公開すると、利用アプリが選べます。

## テストでチャンネルの登録を確認する

登録されたチャンネルは、ドライバーの `getChannels()` で `Collection` として取得できます。キーはチャンネル名そのものです。

```php theme={null}
use Illuminate\Support\Facades\Broadcast;

public function test_package_registers_order_channel(): void
{
    $channels = Broadcast::driver()->getChannels();

    $this->assertTrue($channels->has('acme.orders.{order}'));
}
```

認可の結果は、`/broadcasting/auth` へのHTTPテストで確認します。`null` と `log` のドライバーは `auth()` が何も返さないため、このテストには使えません。チャンネルはアプリの起動時に既定のドライバーへ登録されるため、テスト用の `phpunit.xml` などで `BROADCAST_CONNECTION` を `reverb` や `pusher` に設定してから、認証付きのリクエストを送ります。

```php theme={null}
use Acme\Orders\Models\Order;
use App\Models\User;

public function test_owner_can_join_order_channel(): void
{
    $user = User::factory()->create();
    $order = Order::factory()->for($user, 'owner')->create();

    $this->actingAs($user)
        ->post('/broadcasting/auth', [
            'channel_name' => 'private-acme.orders.'.$order->id,
            'socket_id' => '1234.5678',
        ])
        ->assertOk();
}
```

> [!TIP]
>
> テストアプリで `/broadcasting/auth` が登録されている必要があります。このルートは `Broadcast::routes()` が登録し、`BroadcastManager::channelRoutes()` は同じ処理を `routes()` に任せるだけです。ルートがキャッシュされている場合、`routes()` は何もしません。

## 保守時の注意

* チャンネル名は公開された契約です。名前を変更すると、既存のクライアントが購読できなくなります。
* クラスベースのチャンネルは、`Container` で解決されます。コンストラクターの依存は、コンテナで解決できる型にしてください。

## まとめ

パッケージのチャンネル認可は、`boot()` で `Broadcast::channel()` を登録すれば動きます。利用アプリの `routes/channels.php` が同じ名前を後から登録すると、パッケージの認可は置き換えられます。チャンネル名に接頭辞を付け、判定をクラスに分け、HTTPテストで `/broadcasting/auth` の結果を確認すると、保守しやすくなります。

## 参照した一次情報

* [Laravel公式ドキュメント: ブロードキャスト](https://github.com/laravel/docs/blob/13.x/broadcasting.md)
* [Framework v13.42.0: Broadcaster](https://github.com/laravel/framework/blob/v13.42.0/src/Illuminate/Broadcasting/Broadcasters/Broadcaster.php)
* [Framework v13.42.0: UsePusherChannelConventions](https://github.com/laravel/framework/blob/v13.42.0/src/Illuminate/Broadcasting/Broadcasters/UsePusherChannelConventions.php)
* [Framework v13.42.0: BroadcastManager](https://github.com/laravel/framework/blob/v13.42.0/src/Illuminate/Broadcasting/BroadcastManager.php)
* [Framework v13.42.0: ApplicationBuilder](https://github.com/laravel/framework/blob/v13.42.0/src/Illuminate/Foundation/Configuration/ApplicationBuilder.php)
* [Framework v13.42.0: BroadcastController](https://github.com/laravel/framework/blob/v13.42.0/src/Illuminate/Broadcasting/BroadcastController.php)
* [Framework v13.42.0: channel:list コマンド](https://github.com/laravel/framework/blob/v13.42.0/src/Illuminate/Foundation/Console/ChannelListCommand.php)


## Related topics

- [ブロードキャスト](https://kawax.biz/jp/broadcasting.md)
- [Mercureブロードキャストドライバーの内部構造](https://kawax.biz/jp/advanced/mercure-broadcasting.md)
- [Laravel AI SDK](https://kawax.biz/jp/ai-sdk.md)
- [パッケージの認可ポリシー登録と上書き](https://kawax.biz/jp/advanced/package-authorization.md)
- [Laravel Reverb](https://kawax.biz/jp/reverb.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.