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

# 廣播

> 說明如何使用 Laravel 的廣播與 Laravel Reverb，透過 WebSocket 實作即時通訊。

## 廣播是什麼

透過 WebSocket，伺服器可以將資料即時傳送給客戶端。
Laravel 的廣播機制會將伺服器端的事件透過 WebSocket 傳送到前端的 JavaScript。

例如，當訂單狀態改變時，即使不重新載入頁面也能立即在瀏覽器上顯示更新。
Laravel 廣播的強項在於伺服器端的事件名稱與資料，可以原封不動地共享給客戶端。

<Info>
  廣播是建立在 Laravel 的事件系統之上的。
  建議先理解[事件與監聽器](/zh-TW/events)的基本概念。
</Info>

```mermaid theme={null}
flowchart LR
    A["伺服器端<br>觸發事件<br>dispatch()"] --> B["廣播<br>驅動<br>(Reverb 等)"]
    B --> C["WebSocket<br>伺服器"]
    C --> D["頻道<br>授權檢查"]
    D --> E["客戶端<br>Laravel Echo"]
    E --> F["UI 即時<br>更新"]
```

## 設定

在新的 Laravel 應用程式中，廣播預設為停用。
請使用 `install:broadcasting` Artisan 指令啟用。

```shell theme={null}
php artisan install:broadcasting
```

執行此指令後，會提示你選擇要使用的廣播服務，並產生 `config/broadcasting.php` 與 `routes/channels.php`。

## Laravel Reverb

在 Laravel 11 以後，推薦使用官方的 WebSocket 伺服器 **Laravel Reverb**。
Reverb 屬於自架方案，無須額外的外部服務就能實現即時通訊。

### 安裝

在 `install:broadcasting` 指令加上 `--reverb` 選項，即可一次完成 Reverb 所需 Composer 套件與 NPM 套件的安裝，以及 `.env` 設定。

```shell theme={null}
php artisan install:broadcasting --reverb
```

若要手動安裝，可先透過 Composer 加入套件，再執行安裝指令。

```shell theme={null}
composer require laravel/reverb

php artisan reverb:install
```

### `.env` 的主要設定值

```ini theme={null}
BROADCAST_CONNECTION=reverb

REVERB_APP_ID=my-app-id
REVERB_APP_KEY=my-app-key
REVERB_APP_SECRET=my-app-secret
REVERB_HOST=localhost
REVERB_PORT=8080
REVERB_SCHEME=http

VITE_REVERB_APP_KEY="${REVERB_APP_KEY}"
VITE_REVERB_HOST="${REVERB_HOST}"
VITE_REVERB_PORT="${REVERB_PORT}"
VITE_REVERB_SCHEME="${REVERB_SCHEME}"
```

### 啟動 Reverb 伺服器

```shell theme={null}
php artisan reverb:start
```

在正式環境中，請透過 Supervisor 等程序管理器以常駐程式方式管理。

<Tip>
  廣播事件是透過佇列處理的。
  除了 Reverb 伺服器外，也需要另外啟動佇列 worker。

  ```shell theme={null}
  php artisan queue:work
  ```
</Tip>

## 建立廣播事件

### 產生事件類別

```shell theme={null}
php artisan make:event OrderShipmentStatusUpdated
```

在產生的事件類別中實作 `ShouldBroadcast` 介面。

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

namespace App\Events;

use App\Models\Order;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Queue\SerializesModels;

class OrderShipmentStatusUpdated implements ShouldBroadcast
{
    use InteractsWithSockets, SerializesModels;

    public function __construct(
        public Order $order,
    ) {}

    /**
     * 回傳要廣播事件的頻道
     */
    public function broadcastOn(): Channel
    {
        return new PrivateChannel('orders.' . $this->order->id);
    }
}
```

只要實作 `ShouldBroadcast`，事件被觸發時就會自動透過佇列進行廣播。

### 自訂廣播資料

預設情況下，事件類別的所有 `public` 屬性都會包含在廣播的 payload 中。
若要限縮傳送的資料，請定義 `broadcastWith` 方法。

```php theme={null}
public function broadcastWith(): array
{
    return [
        'order_id' => $this->order->id,
        'status'   => $this->order->status,
    ];
}
```

### 自訂廣播名稱

預設情況下，類別名稱即為事件名稱。
可透過 `broadcastAs` 方法指定自訂名稱。

```php theme={null}
public function broadcastAs(): string
{
    return 'order.status.updated';
}
```

前端監聽時，請在名稱前加上 `.` 以停用應用程式的命名空間前綴。

```js theme={null}
Echo.private(`orders.${orderId}`)
    .listen('.order.status.updated', (e) => {
        console.log(e);
    });
```

## 頻道類型

| 頻道           | 類別                | 說明                    |
| ------------ | ----------------- | --------------------- |
| **Public**   | `Channel`         | 無須認證，任何人都可訂閱          |
| **Private**  | `PrivateChannel`  | 僅限已認證使用者，需要授權邏輯       |
| **Presence** | `PresenceChannel` | Private 的延伸，可取得頻道成員清單 |

```php theme={null}
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;

// Public 頻道
public function broadcastOn(): Channel
{
    return new Channel('posts');
}

// Private 頻道
public function broadcastOn(): Channel
{
    return new PrivateChannel('orders.' . $this->order->id);
}

// Presence 頻道
public function broadcastOn(): Channel
{
    return new PresenceChannel('rooms.' . $this->room->id);
}
```

若要對多個頻道廣播，請以陣列形式回傳。

```php theme={null}
public function broadcastOn(): array
{
    return [
        new PrivateChannel('orders.' . $this->order->id),
        new Channel('admin.orders'),
    ];
}
```

## 頻道授權

Private 頻道與 Presence 頻道會在訂閱前於伺服器端進行授權檢查。

### routes/channels.php

透過 `install:broadcasting` 指令產生的 `routes/channels.php` 中定義授權 callback。

```php theme={null}
use App\Models\Order;
use App\Models\User;
use Illuminate\Support\Facades\Broadcast;

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

callback 的第 1 個參數是已認證的使用者，之後的參數則對應到頻道名稱中的萬用字元。
回傳 `true` 或 truthy 值代表授權成功，回傳 `false` 則會被拒絕。

<Info>
  也可以使用路由模型繫結。
  若將頻道名稱設為 `orders.{order}`，則會傳入 `Order` 模型實例。
</Info>

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

### 透過頻道類別進行授權

當頻道變多時，可透過頻道類別加以整理。

```shell theme={null}
php artisan make:channel OrderChannel
```

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

namespace App\Broadcasting;

use App\Models\Order;
use App\Models\User;

class OrderChannel
{
    public function join(User $user, Order $order): bool
    {
        return $user->id === $order->user_id;
    }
}
```

在 `routes/channels.php` 中註冊。

```php theme={null}
use App\Broadcasting\OrderChannel;

Broadcast::channel('orders.{order}', OrderChannel::class);
```

## 觸發事件

實作了 `ShouldBroadcast` 的事件與一般事件的觸發方式相同。

```php theme={null}
use App\Events\OrderShipmentStatusUpdated;

OrderShipmentStatusUpdated::dispatch($order);
```

若只想廣播給自己以外的其他使用者，請使用 `toOthers`。

```php theme={null}
broadcast(new OrderShipmentStatusUpdated($order))->toOthers();
```

<Warning>
  使用 `toOthers` 時，事件類別必須使用 `InteractsWithSockets` trait。
</Warning>

## 前端接收

### 設定 Laravel Echo

使用 Reverb 時，請在 `resources/js/bootstrap.js` 中設定 Echo 實例。

```shell theme={null}
npm install --save-dev laravel-echo pusher-js
```

```js theme={null}
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 ?? 80,
    wssPort: import.meta.env.VITE_REVERB_PORT ?? 443,
    forceTLS: (import.meta.env.VITE_REVERB_SCHEME ?? 'https') === 'https',
    enabledTransports: ['ws', 'wss'],
});
```

### 監聽事件

```js theme={null}
// Public 頻道
Echo.channel('posts')
    .listen('PostPublished', (e) => {
        console.log(e.post);
    });

// Private 頻道
Echo.private(`orders.${orderId}`)
    .listen('OrderShipmentStatusUpdated', (e) => {
        console.log(e.order);
    });

// Presence 頻道
Echo.join(`rooms.${roomId}`)
    .here((users) => {
        console.log('目前的成員：', users);
    })
    .joining((user) => {
        console.log(user.name, '加入了頻道');
    })
    .leaving((user) => {
        console.log(user.name, '離開了頻道');
    })
    .listen('MessagePosted', (e) => {
        console.log(e.message);
    });
```

### 使用 React / Vue Hook

若你使用 React 或 Vue 的入門套件，可以透過專屬 Hook 讓程式更精簡。

```js theme={null}
import { useEcho } from "@laravel/echo-react";

// Private 頻道
useEcho(
    `orders.${orderId}`,
    "OrderShipmentStatusUpdated",
    (e) => {
        console.log(e.order);
    },
);

// Public 頻道
import { useEchoPublic } from "@laravel/echo-react";

useEchoPublic("posts", "PostPublished", (e) => {
    console.log(e.post);
});
```

`useEcho` Hook 會在元件卸載時自動退出頻道。

<Tip>
  在建置前端資源之前，請確認 `.env` 內的 `VITE_REVERB_*` 變數已正確設定。

  ```shell theme={null}
  npm run build
  ```
</Tip>

## 實戰範例：訂單狀態即時更新

<Steps>
  <Step title="啟用廣播">
    ```shell theme={null}
    php artisan install:broadcasting --reverb
    ```

    啟動 Reverb 伺服器與佇列 worker。

    ```shell theme={null}
    php artisan reverb:start
    php artisan queue:work
    ```
  </Step>

  <Step title="建立事件類別">
    ```shell theme={null}
    php artisan make:event OrderShipmentStatusUpdated
    ```

    編輯 `app/Events/OrderShipmentStatusUpdated.php`。

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

    namespace App\Events;

    use App\Models\Order;
    use Illuminate\Broadcasting\Channel;
    use Illuminate\Broadcasting\InteractsWithSockets;
    use Illuminate\Broadcasting\PrivateChannel;
    use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
    use Illuminate\Queue\SerializesModels;

    class OrderShipmentStatusUpdated implements ShouldBroadcast
    {
        use InteractsWithSockets, SerializesModels;

        public function __construct(
            public Order $order,
        ) {}

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

        public function broadcastWith(): array
        {
            return [
                'order_id' => $this->order->id,
                'status'   => $this->order->status,
            ];
        }
    }
    ```
  </Step>

  <Step title="定義頻道授權">
    在 `routes/channels.php` 加入授權邏輯。

    ```php theme={null}
    use App\Models\Order;
    use App\Models\User;
    use Illuminate\Support\Facades\Broadcast;

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

  <Step title="觸發事件">
    在更新訂單狀態的控制器或 Job 中觸發事件。

    ```php theme={null}
    use App\Events\OrderShipmentStatusUpdated;

    $order->update(['status' => 'shipped']);

    OrderShipmentStatusUpdated::dispatch($order);
    ```
  </Step>

  <Step title="前端進行監聽">
    在 Blade 樣板中使用內嵌 script，或於 React / Vue 元件中接收。

    ```js theme={null}
    Echo.private(`orders.${orderId}`)
        .listen('OrderShipmentStatusUpdated', (e) => {
            document.getElementById('status').textContent = e.status;
        });
    ```
  </Step>
</Steps>

## 下一步

<Card title="Laravel Reverb" href="/zh-TW/reverb">
  進一步了解 Reverb 伺服器的設定、正式環境維運與擴展。
</Card>

<Card title="佇列與工作" href="/zh-TW/queues">
  廣播是透過佇列處理的，請確認佇列的設定與運行方式。
</Card>

<Card title="事件與監聽器" href="/zh-TW/events">
  進一步學習作為廣播基礎的 Laravel 事件系統。
</Card>


## Related topics

- [Laravel AI SDK](/zh-TW/ai-sdk.md)
- [Laravel 11 以後的新應用程式結構 FAQ](/zh-TW/advanced/app-structure-faq.md)
- [SessionEvent](/zh-TW/packages/laravel-copilot-sdk/session-event.md)
- [Laravel 新程式碼分析生態系 — surveyor / ranger / roster](/zh-TW/blog/laravel-ecosystem-analysis.md)
- [工具](/zh-TW/packages/laravel-copilot-sdk/tools.md)
