> ## 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/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 实时更新"]
```

## 配置

新项目默认关闭广播。用 `install:broadcasting` 启用：

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

命令会提示你选择要使用的广播服务，并生成 `config/broadcasting.php` 与 `routes/channels.php`。

## Laravel Reverb

Laravel 11 及以后推荐使用官方 WebSocket 服务器 **Laravel Reverb**。
Reverb 支持自托管，无需外部服务即可实现实时通信。

### 安装

在 `install:broadcasting` 加 `--reverb` 可一次完成 Composer / NPM 依赖安装与 `.env` 配置：

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

手动安装：

```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';
}
```

前端监听时使用 `.` 前缀以关闭 Laravel 命名空间前缀：

```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` 中定义授权回调：

```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;
});
```

回调第一个参数是当前用户，之后是频道名中的通配符。
返回 `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);
```

只广播给他人（排除自己）：

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

<Warning>
  使用 `toOthers` 需要事件类 use `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` 会在组件卸载时自动离开频道。

<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="定义频道授权">
    ```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="触发事件">
    ```php theme={null}
    use App\Events\OrderShipmentStatusUpdated;

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

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

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

## 下一步

<Card title="Laravel Reverb" href="/zh/reverb">
  查看 Reverb 服务器的部署、生产运维与扩展详情。
</Card>

<Card title="队列与任务" href="/zh/queues">
  广播依赖队列处理。了解队列的配置与运维。
</Card>

<Card title="事件与监听器" href="/zh/events">
  学习作为广播基础的 Laravel 事件系统。
</Card>


## Related topics

- [Laravel AI SDK](/zh/ai-sdk.md)
- [Laravel Reverb](/zh/reverb.md)
- [Laravel 11 之后新应用结构 FAQ](/zh/advanced/app-structure-faq.md)
- [SessionEvent](/zh/packages/laravel-copilot-sdk/session-event.md)
- [流式输出](/zh/packages/laravel-copilot-sdk/streaming.md)
