跳转至内容

广播

简介

在许多现代 Web 应用中,WebSocket 被用于实现实时、动态更新的用户界面。当服务器端数据更新时,通常会通过 WebSocket 连接发送一条消息,由客户端进行处理。与不断轮询服务器以获取 UI 所需的更新数据相比,WebSocket 提供了一种更高效的选择。

例如,假设你的应用程序能够将用户数据导出为 CSV 文件并发送到其邮箱。由于创建 CSV 文件需要几分钟时间,你选择在一个 队列任务 中生成并发送邮件。当 CSV 创建并发送完毕后,我们可以使用事件广播分发一个 App\Events\UserDataExported 事件,该事件会被应用程序的 JavaScript 端接收。收到事件后,我们可以向用户显示一条提示信息,告知 CSV 已发送至其邮箱,而无需刷新页面。

为了协助你构建此类功能,Laravel 使得通过 WebSocket 连接“广播”你的服务端 Laravel 事件 变得非常简单。广播 Laravel 事件允许你在服务端 Laravel 应用和客户端 JavaScript 应用之间共享相同的事件名称和数据。

广播的核心概念很简单:客户端在前端订阅命名频道,而你的 Laravel 后端则向这些频道广播事件。这些事件可以包含任何你希望在前端获取的额外数据。

支持的驱动

默认情况下,Laravel 包含三种服务端广播驱动供选择:Laravel ReverbPusher ChannelsAbly

在深入了解事件广播之前,请确保你已经阅读了 Laravel 关于 事件和监听器 的文档。

快速入门

默认情况下,新创建的 Laravel 应用并未启用广播功能。你可以使用 install:broadcasting Artisan 命令来启用广播:

1php artisan install:broadcasting

install:broadcasting 命令会询问你希望使用哪种事件广播服务。此外,它还会创建 config/broadcasting.php 配置文件和 routes/channels.php 文件,你可以在这些文件中注册应用的广播授权路由和回调。

Laravel 开箱即用地支持多种广播驱动:Laravel ReverbPusher ChannelsAbly,以及用于本地开发和调试的 log 驱动。此外,还包含一个 null 驱动,用于在测试时禁用广播。config/broadcasting.php 配置文件中包含了每种驱动的配置示例。

应用程序所有的事件广播配置都存储在 config/broadcasting.php 配置文件中。如果你的应用中不存在此文件,请放心,运行 install:broadcasting Artisan 命令时它会自动创建。

后续步骤

启用事件广播后,你就可以进一步学习 定义广播事件监听事件 了。如果你正在使用 Laravel 的 React 或 Vue 入门套件,你可以使用 Echo 的 useEcho hook 来监听事件。

在广播任何事件之前,你应该首先配置并运行一个 队列工作器。所有的事件广播都是通过队列任务完成的,以确保应用的响应时间不会因事件广播而受到严重影响。

服务端安装

要开始使用 Laravel 的事件广播,我们需要在 Laravel 应用中进行一些配置,并安装一些包。

事件广播由服务端广播驱动实现,它将你的 Laravel 事件进行广播,以便 Laravel Echo(一个 JavaScript 库)能够在浏览器客户端接收到。别担心,我们将逐步介绍安装过程的每个部分。

Reverb

若要在使用 Reverb 作为事件广播器时快速启用对 Laravel 广播功能的支持,请使用 --reverb 选项调用 install:broadcasting Artisan 命令。该 Artisan 命令将安装 Reverb 所需的 Composer 和 NPM 包,并使用相应的变量更新应用的 .env 文件。

1php artisan install:broadcasting --reverb

手动安装

运行 install:broadcasting 命令时,系统会提示你安装 Laravel Reverb。当然,你也可以使用 Composer 包管理器手动安装 Reverb:

1composer require laravel/reverb

安装包后,你可以运行 Reverb 的安装命令来发布配置文件、添加 Reverb 所需的环境变量,并启用应用中的事件广播:

1php artisan reverb:install

你可以在 Reverb 文档 中找到详细的 Reverb 安装和使用说明。

Pusher Channels

若要在使用 Pusher 作为事件广播器时快速启用对 Laravel 广播功能的支持,请使用 --pusher 选项调用 install:broadcasting Artisan 命令。该命令将询问你的 Pusher 凭据,安装 Pusher PHP 和 JavaScript SDK,并使用相应的变量更新应用的 .env 文件。

1php artisan install:broadcasting --pusher

手动安装

若要手动安装 Pusher 支持,你应该使用 Composer 包管理器安装 Pusher Channels PHP SDK:

1composer require pusher/pusher-php-server

接下来,你应该在 config/broadcasting.php 配置文件中配置你的 Pusher Channels 凭据。该文件中已包含一个 Pusher Channels 配置示例,允许你快速指定密钥、秘钥和应用 ID。通常,你应该在应用的 .env 文件中配置这些凭据。

1PUSHER_APP_ID="your-pusher-app-id"
2PUSHER_APP_KEY="your-pusher-key"
3PUSHER_APP_SECRET="your-pusher-secret"
4PUSHER_HOST=
5PUSHER_PORT=443
6PUSHER_SCHEME="https"
7PUSHER_APP_CLUSTER="mt1"

config/broadcasting.php 文件中的 pusher 配置还允许你指定 Channels 支持的其他 options,例如 cluster(集群)。

然后,在应用的 .env 文件中将 BROADCAST_CONNECTION 环境变量设置为 pusher

1BROADCAST_CONNECTION=pusher

最后,你可以安装并配置 Laravel Echo 了,它将负责在客户端接收广播事件。

Ably

以下文档讨论了如何以“Pusher 兼容”模式使用 Ably。但是,Ably 团队建议并维护着一个能够充分利用 Ably 独特功能的广播器和 Echo 客户端。关于使用 Ably 维护的驱动程序的更多信息,请 查阅 Ably 的 Laravel 广播器文档

若要在使用 Ably 作为事件广播器时快速启用对 Laravel 广播功能的支持,请使用 --ably 选项调用 install:broadcasting Artisan 命令。该命令将询问你的 Ably 凭据,安装 Ably PHP 和 JavaScript SDK,并使用相应的变量更新应用的 .env 文件。

1php artisan install:broadcasting --ably

在继续之前,你应该在 Ably 应用设置中启用 Pusher 协议支持。你可以在 Ably 应用设置面板的“Protocol Adapter Settings”(协议适配器设置)部分启用此功能。

手动安装

若要手动安装 Ably 支持,你应该使用 Composer 包管理器安装 Ably PHP SDK:

1composer require ably/ably-php

接下来,你应该在 config/broadcasting.php 配置文件中配置你的 Ably 凭据。该文件中已包含一个 Ably 配置示例,允许你快速指定 key。通常,此值应通过 ABLY_KEY 环境变量 进行设置。

1ABLY_KEY=your-ably-key

然后,在应用的 .env 文件中将 BROADCAST_CONNECTION 环境变量设置为 ably

1BROADCAST_CONNECTION=ably

最后,你可以安装并配置 Laravel Echo 了,它将负责在客户端接收广播事件。

客户端安装

Reverb

Laravel Echo 是一个 JavaScript 库,它能让你轻松订阅频道并监听服务端广播驱动所广播的事件。

当通过 install:broadcasting Artisan 命令安装 Laravel Reverb 时,Reverb 和 Echo 的脚手架及配置将自动注入到你的应用中。然而,如果你希望手动配置 Laravel Echo,可以按照以下说明进行操作。

手动安装

若要为应用的前端手动配置 Laravel Echo,首先安装 pusher-js 包,因为 Reverb 使用 Pusher 协议进行 WebSocket 订阅、频道和消息传输:

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

Echo 安装完成后,你就可以在应用的 JavaScript 中创建一个新的 Echo 实例了。一个很好的做法是在 Laravel 框架自带的 resources/js/bootstrap.js 文件底部进行配置:

1import Echo from 'laravel-echo';
2 
3import Pusher from 'pusher-js';
4window.Pusher = Pusher;
5 
6window.Echo = new Echo({
7 broadcaster: 'reverb',
8 key: import.meta.env.VITE_REVERB_APP_KEY,
9 wsHost: import.meta.env.VITE_REVERB_HOST,
10 wsPort: import.meta.env.VITE_REVERB_PORT ?? 80,
11 wssPort: import.meta.env.VITE_REVERB_PORT ?? 443,
12 forceTLS: (import.meta.env.VITE_REVERB_SCHEME ?? 'https') === 'https',
13 enabledTransports: ['ws', 'wss'],
14});
1import { configureEcho } from "@laravel/echo-react";
2 
3configureEcho({
4 broadcaster: "reverb",
5 // key: import.meta.env.VITE_REVERB_APP_KEY,
6 // wsHost: import.meta.env.VITE_REVERB_HOST,
7 // wsPort: import.meta.env.VITE_REVERB_PORT,
8 // wssPort: import.meta.env.VITE_REVERB_PORT,
9 // forceTLS: (import.meta.env.VITE_REVERB_SCHEME ?? 'https') === 'https',
10 // enabledTransports: ['ws', 'wss'],
11});
1import { configureEcho } from "@laravel/echo-vue";
2 
3configureEcho({
4 broadcaster: "reverb",
5 // key: import.meta.env.VITE_REVERB_APP_KEY,
6 // wsHost: import.meta.env.VITE_REVERB_HOST,
7 // wsPort: import.meta.env.VITE_REVERB_PORT,
8 // wssPort: import.meta.env.VITE_REVERB_PORT,
9 // forceTLS: (import.meta.env.VITE_REVERB_SCHEME ?? 'https') === 'https',
10 // enabledTransports: ['ws', 'wss'],
11});

接下来,你应该编译应用的资源:

1npm run build

Laravel Echo 的 reverb 广播器需要 laravel-echo v1.16.0+ 版本。

Pusher Channels

Laravel Echo 是一个 JavaScript 库,它能让你轻松订阅频道并监听服务端广播驱动所广播的事件。

当通过 install:broadcasting --pusher Artisan 命令安装广播支持时,Pusher 和 Echo 的脚手架及配置将自动注入到你的应用中。如果你希望手动配置 Laravel Echo,可以按照以下说明进行操作。

手动安装

若要为应用的前端手动配置 Laravel Echo,首先安装 laravel-echopusher-js 包,它们使用 Pusher 协议进行 WebSocket 订阅、频道和消息传输:

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

Echo 安装完成后,你就可以在应用的 resources/js/bootstrap.js 文件中创建一个新的 Echo 实例了:

1import Echo from 'laravel-echo';
2 
3import Pusher from 'pusher-js';
4window.Pusher = Pusher;
5 
6window.Echo = new Echo({
7 broadcaster: 'pusher',
8 key: import.meta.env.VITE_PUSHER_APP_KEY,
9 cluster: import.meta.env.VITE_PUSHER_APP_CLUSTER,
10 forceTLS: true
11});
1import { configureEcho } from "@laravel/echo-react";
2 
3configureEcho({
4 broadcaster: "pusher",
5 // key: import.meta.env.VITE_PUSHER_APP_KEY,
6 // cluster: import.meta.env.VITE_PUSHER_APP_CLUSTER,
7 // forceTLS: true,
8 // wsHost: import.meta.env.VITE_PUSHER_HOST,
9 // wsPort: import.meta.env.VITE_PUSHER_PORT,
10 // wssPort: import.meta.env.VITE_PUSHER_PORT,
11 // enabledTransports: ["ws", "wss"],
12});
1import { configureEcho } from "@laravel/echo-vue";
2 
3configureEcho({
4 broadcaster: "pusher",
5 // key: import.meta.env.VITE_PUSHER_APP_KEY,
6 // cluster: import.meta.env.VITE_PUSHER_APP_CLUSTER,
7 // forceTLS: true,
8 // wsHost: import.meta.env.VITE_PUSHER_HOST,
9 // wsPort: import.meta.env.VITE_PUSHER_PORT,
10 // wssPort: import.meta.env.VITE_PUSHER_PORT,
11 // enabledTransports: ["ws", "wss"],
12});

接下来,你应该在应用的 .env 文件中为 Pusher 环境变量定义相应的值。如果这些变量在你的 .env 文件中尚不存在,请将它们添加进去:

1PUSHER_APP_ID="your-pusher-app-id"
2PUSHER_APP_KEY="your-pusher-key"
3PUSHER_APP_SECRET="your-pusher-secret"
4PUSHER_HOST=
5PUSHER_PORT=443
6PUSHER_SCHEME="https"
7PUSHER_APP_CLUSTER="mt1"
8 
9VITE_APP_NAME="${APP_NAME}"
10VITE_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
11VITE_PUSHER_HOST="${PUSHER_HOST}"
12VITE_PUSHER_PORT="${PUSHER_PORT}"
13VITE_PUSHER_SCHEME="${PUSHER_SCHEME}"
14VITE_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"

根据应用需求调整 Echo 配置后,你就可以编译应用的资源了:

1npm run build

若要了解更多关于编译应用 JavaScript 资源的信息,请查阅 Vite 文档。

使用现有的客户端实例

如果你已经有一个预先配置好的 Pusher Channels 客户端实例,并希望 Echo 使用它,可以通过 client 配置选项将其传递给 Echo:

1import Echo from 'laravel-echo';
2import Pusher from 'pusher-js';
3 
4const options = {
5 broadcaster: 'pusher',
6 key: import.meta.env.VITE_PUSHER_APP_KEY
7}
8 
9window.Echo = new Echo({
10 ...options,
11 client: new Pusher(options.key, options)
12});

Ably

以下文档讨论了如何以“Pusher 兼容”模式使用 Ably。但是,Ably 团队建议并维护着一个能够充分利用 Ably 独特功能的广播器和 Echo 客户端。关于使用 Ably 维护的驱动程序的更多信息,请 查阅 Ably 的 Laravel 广播器文档

Laravel Echo 是一个 JavaScript 库,它能让你轻松订阅频道并监听服务端广播驱动所广播的事件。

当通过 install:broadcasting --ably Artisan 命令安装广播支持时,Ably 和 Echo 的脚手架及配置将自动注入到你的应用中。如果你希望手动配置 Laravel Echo,可以按照以下说明进行操作。

手动安装

若要为应用的前端手动配置 Laravel Echo,首先安装 laravel-echopusher-js 包,它们使用 Pusher 协议进行 WebSocket 订阅、频道和消息传输:

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

在继续之前,你应该在 Ably 应用设置中启用 Pusher 协议支持。你可以在 Ably 应用设置面板的“Protocol Adapter Settings”(协议适配器设置)部分启用此功能。

Echo 安装完成后,你就可以在应用的 resources/js/bootstrap.js 文件中创建一个新的 Echo 实例了:

1import Echo from 'laravel-echo';
2 
3import Pusher from 'pusher-js';
4window.Pusher = Pusher;
5 
6window.Echo = new Echo({
7 broadcaster: 'pusher',
8 key: import.meta.env.VITE_ABLY_PUBLIC_KEY,
9 wsHost: 'realtime-pusher.ably.io',
10 wsPort: 443,
11 disableStats: true,
12 encrypted: true,
13});
1import { configureEcho } from "@laravel/echo-react";
2 
3configureEcho({
4 broadcaster: "ably",
5 // key: import.meta.env.VITE_ABLY_PUBLIC_KEY,
6 // wsHost: "realtime-pusher.ably.io",
7 // wsPort: 443,
8 // disableStats: true,
9 // encrypted: true,
10});
1import { configureEcho } from "@laravel/echo-vue";
2 
3configureEcho({
4 broadcaster: "ably",
5 // key: import.meta.env.VITE_ABLY_PUBLIC_KEY,
6 // wsHost: "realtime-pusher.ably.io",
7 // wsPort: 443,
8 // disableStats: true,
9 // encrypted: true,
10});

你可能已经注意到,我们的 Ably Echo 配置引用了一个 VITE_ABLY_PUBLIC_KEY 环境变量。该变量的值应该是你的 Ably 公钥。公钥是你 Ably 密钥中 : 字符之前的那部分。

根据需求调整 Echo 配置后,你就可以编译应用的资源了:

1npm run dev

若要了解更多关于编译应用 JavaScript 资源的信息,请查阅 Vite 文档。

概念概览

Laravel 的事件广播允许你使用基于驱动的 WebSocket 方法,将服务端 Laravel 事件广播到客户端 JavaScript 应用。目前,Laravel 自带 Laravel ReverbPusher ChannelsAbly 驱动。可以使用 Laravel Echo JavaScript 包在客户端轻松消费这些事件。

事件通过“频道”进行广播,可以指定为公共或私有。应用的任何访问者无需身份验证或授权即可订阅公共频道;然而,要订阅私有频道,用户必须经过身份验证并获得该频道的监听授权。

使用示例应用程序

在深入了解事件广播的每个组件之前,我们先以电子商务商店为例进行概览。

在我们的应用中,假设有一个页面允许用户查看其订单的物流状态。同时假设当应用处理物流状态更新时,会触发一个 OrderShipmentStatusUpdated 事件:

1use App\Events\OrderShipmentStatusUpdated;
2 
3OrderShipmentStatusUpdated::dispatch($order);

ShouldBroadcast 接口

当用户查看订单时,我们不希望他们必须刷新页面才能看到状态更新。相反,我们希望在状态更新时将其实时广播给应用。因此,我们需要使用 ShouldBroadcast 接口标记 OrderShipmentStatusUpdated 事件。这将指示 Laravel 在触发事件时进行广播。

1<?php
2 
3namespace App\Events;
4 
5use App\Models\Order;
6use Illuminate\Broadcasting\Channel;
7use Illuminate\Broadcasting\InteractsWithSockets;
8use Illuminate\Broadcasting\PresenceChannel;
9use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
10use Illuminate\Queue\SerializesModels;
11 
12class OrderShipmentStatusUpdated implements ShouldBroadcast
13{
14 /**
15 * The order instance.
16 *
17 * @var \App\Models\Order
18 */
19 public $order;
20}

ShouldBroadcast 接口要求我们的事件定义一个 broadcastOn 方法。该方法负责返回事件应广播到的频道。生成的事件类中已经定义了该方法的空存根,我们只需要填充其细节。我们只希望订单的创建者能够查看状态更新,因此我们将在一个与订单绑定的私有频道上进行广播:

1use Illuminate\Broadcasting\Channel;
2use Illuminate\Broadcasting\PrivateChannel;
3 
4/**
5 * Get the channel the event should broadcast on.
6 */
7public function broadcastOn(): Channel
8{
9 return new PrivateChannel('orders.'.$this->order->id);
10}

如果你希望事件在多个频道上广播,可以返回一个 array

1use Illuminate\Broadcasting\PrivateChannel;
2 
3/**
4 * Get the channels the event should broadcast on.
5 *
6 * @return array<int, \Illuminate\Broadcasting\Channel>
7 */
8public function broadcastOn(): array
9{
10 return [
11 new PrivateChannel('orders.'.$this->order->id),
12 // ...
13 ];
14}

频道授权

记住,用户必须获得监听私有频道的授权。我们可以在应用的 routes/channels.php 文件中定义频道授权规则。在这个例子中,我们需要验证任何试图监听私有频道 orders.1 的用户是否真的是该订单的创建者:

1use App\Models\Order;
2use App\Models\User;
3 
4Broadcast::channel('orders.{orderId}', function (User $user, int $orderId) {
5 return $user->id === Order::findOrNew($orderId)->user_id;
6});

channel 方法接收两个参数:频道名称和一个返回 truefalse 的回调,用于指示用户是否被授权监听该频道。

所有的授权回调都会接收当前已认证的用户作为第一个参数,以及任何额外的通配符参数作为后续参数。在这个例子中,我们使用 {orderId} 占位符来表示频道名称中的“ID”部分是一个通配符。

监听事件广播

接下来,剩下的工作就是在 JavaScript 应用中监听该事件。我们可以使用 Laravel Echo。Laravel Echo 内置的 React 和 Vue Hooks 使得上手非常简单,默认情况下,事件的所有公共属性都将包含在广播事件中:

1import { useEcho } from "@laravel/echo-react";
2 
3useEcho(
4 `orders.${orderId}`,
5 "OrderShipmentStatusUpdated",
6 (e) => {
7 console.log(e.order);
8 },
9);
1<script setup lang="ts">
2import { useEcho } from "@laravel/echo-vue";
3 
4useEcho(
5 `orders.${orderId}`,
6 "OrderShipmentStatusUpdated",
7 (e) => {
8 console.log(e.order);
9 },
10);
11</script>

定义广播事件

要通知 Laravel 某个事件应该被广播,你必须在事件类中实现 Illuminate\Contracts\Broadcasting\ShouldBroadcast 接口。框架生成的所有事件类都已经导入了这个接口,因此你可以轻松地将其添加到任何事件中。

ShouldBroadcast 接口要求你实现一个单一方法:broadcastOn。该方法应该返回一个频道或频道数组。频道应该是 ChannelPrivateChannelPresenceChannel 的实例。Channel 实例代表任何用户都可以订阅的公共频道,而 PrivateChannelsPresenceChannels 则代表需要 频道授权 的私有频道。

1<?php
2 
3namespace App\Events;
4 
5use App\Models\User;
6use Illuminate\Broadcasting\Channel;
7use Illuminate\Broadcasting\InteractsWithSockets;
8use Illuminate\Broadcasting\PresenceChannel;
9use Illuminate\Broadcasting\PrivateChannel;
10use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
11use Illuminate\Queue\SerializesModels;
12 
13class ServerCreated implements ShouldBroadcast
14{
15 use SerializesModels;
16 
17 /**
18 * Create a new event instance.
19 */
20 public function __construct(
21 public User $user,
22 ) {}
23 
24 /**
25 * Get the channels the event should broadcast on.
26 *
27 * @return array<int, \Illuminate\Broadcasting\Channel>
28 */
29 public function broadcastOn(): array
30 {
31 return [
32 new PrivateChannel('user.'.$this->user->id),
33 ];
34 }
35}

实现 ShouldBroadcast 接口后,你只需要像往常一样 触发事件 即可。事件触发后,一个 队列任务 会自动使用指定的广播驱动来广播该事件。

广播名称

默认情况下,Laravel 会使用事件的类名进行广播。但是,你可以通过在事件中定义 broadcastAs 方法来自定义广播名称:

1/**
2 * The event's broadcast name.
3 */
4public function broadcastAs(): string
5{
6 return 'server.created';
7}

如果你通过 broadcastAs 方法自定义了广播名称,请确保在注册监听器时加上前缀 . 字符。这将指示 Echo 不要给事件添加应用命名空间前缀。

1.listen('.server.created', function (e) {
2 // ...
3});

广播数据

当事件被广播时,其所有 public 属性都会自动序列化并作为事件的载荷进行广播,允许你从 JavaScript 应用中访问任何公开数据。所以,如果你的事件包含一个含有 Eloquent 模型的公共 $user 属性,那么事件的广播载荷将是:

1{
2 "user": {
3 "id": 1,
4 "name": "Patrick Stewart"
5 ...
6 }
7}

但是,如果你希望对广播载荷拥有更细粒度的控制,可以在事件中添加一个 broadcastWith 方法。该方法应返回你希望作为事件载荷广播的数据数组:

1/**
2 * Get the data to broadcast.
3 *
4 * @return array<string, mixed>
5 */
6public function broadcastWith(): array
7{
8 return ['id' => $this->user->id];
9}

广播队列

默认情况下,每个广播事件都会被放置在 queue.php 配置文件中指定的默认队列连接的默认队列中。你可以使用事件类上的 ConnectionQueue 属性来自定义广播器使用的队列连接和名称:

1use Illuminate\Queue\Attributes\Connection;
2use Illuminate\Queue\Attributes\Queue;
3 
4#[Connection('redis')]
5#[Queue('default')]
6class ServerCreated implements ShouldBroadcast
7{
8 // ...
9}

或者,你可以通过在事件中定义 broadcastQueue 方法来自定义队列名称:

1/**
2 * The name of the queue on which to place the broadcasting job.
3 */
4public function broadcastQueue(): string
5{
6 return 'default';
7}

如果你希望使用 sync 队列而不是默认的队列驱动来广播事件,可以实现 ShouldBroadcastNow 接口而不是 ShouldBroadcast

1<?php
2 
3namespace App\Events;
4 
5use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;
6 
7class OrderShipmentStatusUpdated implements ShouldBroadcastNow
8{
9 // ...
10}

广播条件

有时你只想在满足特定条件时才广播事件。你可以通过在事件类中添加 broadcastWhen 方法来定义这些条件:

1/**
2 * Determine if this event should broadcast.
3 */
4public function broadcastWhen(): bool
5{
6 return $this->order->value > 100;
7}

广播与数据库事务

当广播事件在数据库事务中被分发时,它们可能在数据库事务提交之前就被队列处理。在这种情况下,你在数据库事务期间对模型或数据库记录所做的任何更新可能尚未反映在数据库中。此外,事务内创建的任何模型或数据库记录可能尚不存在。如果你的事件依赖于这些模型,当处理广播该事件的任务时可能会出现意外错误。

如果你的队列连接的 after_commit 配置选项设置为 false,你仍然可以通过在事件类上实现 ShouldDispatchAfterCommit 接口,来指示特定的广播事件应该在所有打开的数据库事务提交后才被分发。

1<?php
2 
3namespace App\Events;
4 
5use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
6use Illuminate\Contracts\Events\ShouldDispatchAfterCommit;
7use Illuminate\Queue\SerializesModels;
8 
9class ServerCreated implements ShouldBroadcast, ShouldDispatchAfterCommit
10{
11 use SerializesModels;
12}

要了解更多关于解决这些问题的信息,请查看关于排队作业与数据库事务的文档。

频道授权

私有频道要求你授权当前已认证的用户确实可以监听该频道。这是通过向 Laravel 应用发起一个带有频道名称的 HTTP 请求来实现的,并由应用确定用户是否可以监听该频道。使用 Laravel Echo 时,授权订阅私有频道的 HTTP 请求会自动发起。

当安装广播功能时,Laravel 会尝试自动注册 /broadcasting/auth 路由来处理授权请求。如果 Laravel 未能自动注册这些路由,你可以在应用的 /bootstrap/app.php 文件中手动注册它们:

1->withRouting(
2 web: __DIR__.'/../routes/web.php',
3 channels: __DIR__.'/../routes/channels.php',
4 health: '/up',
5)

定义授权回调

接下来,我们需要定义逻辑来确定当前已认证的用户是否可以监听某个给定频道。这在 install:broadcasting Artisan 命令创建的 routes/channels.php 文件中完成。在该文件中,你可以使用 Broadcast::channel 方法来注册频道授权回调:

1use App\Models\User;
2 
3Broadcast::channel('orders.{orderId}', function (User $user, int $orderId) {
4 return $user->id === Order::findOrNew($orderId)->user_id;
5});

channel 方法接收两个参数:频道名称和一个返回 truefalse 的回调,用于指示用户是否被授权监听该频道。

所有的授权回调都会接收当前已认证的用户作为第一个参数,以及任何额外的通配符参数作为后续参数。在这个例子中,我们使用 {orderId} 占位符来表示频道名称中的“ID”部分是一个通配符。

你可以使用 channel:list Artisan 命令查看应用中广播授权回调的列表。

1php artisan channel:list

授权回调模型绑定

就像 HTTP 路由一样,频道路由也可以利用隐式和显式 路由模型绑定。例如,你可以请求一个实际的 Order 模型实例,而不是接收字符串或数字订单 ID:

1use App\Models\Order;
2use App\Models\User;
3 
4Broadcast::channel('orders.{order}', function (User $user, Order $order) {
5 return $user->id === $order->user_id;
6});

与 HTTP 路由模型绑定不同,频道模型绑定不支持自动 隐式模型绑定作用域。不过,这很少会成为问题,因为大多数频道都可以根据单个模型的唯一主键进行作用域划分。

授权回调身份验证

私有和 Presence 广播频道通过应用的默认身份验证守卫(guard)验证当前用户。如果用户未认证,频道授权将自动被拒绝,且授权回调永远不会执行。然而,如有必要,你可以指定多个自定义守卫来验证传入请求:

1Broadcast::channel('channel', function () {
2 // ...
3}, ['guards' => ['web', 'admin']]);

定义频道类

如果你的应用正在消费许多不同的频道,routes/channels.php 文件可能会变得臃肿。因此,你可以使用频道类来代替闭包以授权频道。要生成频道类,请使用 make:channel Artisan 命令。该命令将在 App/Broadcasting 目录下创建一个新的频道类。

1php artisan make:channel OrderChannel

接下来,在 routes/channels.php 文件中注册你的频道:

1use App\Broadcasting\OrderChannel;
2 
3Broadcast::channel('orders.{order}', OrderChannel::class);

最后,你可以将频道的授权逻辑放在频道类的 join 方法中。这个 join 方法将包含你通常放在频道授权闭包中的逻辑。你也可以利用频道模型绑定:

1<?php
2 
3namespace App\Broadcasting;
4 
5use App\Models\Order;
6use App\Models\User;
7 
8class OrderChannel
9{
10 /**
11 * Create a new channel instance.
12 */
13 public function __construct() {}
14 
15 /**
16 * Authenticate the user's access to the channel.
17 */
18 public function join(User $user, Order $order): array|bool
19 {
20 return $user->id === $order->user_id;
21 }
22}

像 Laravel 中的其他许多类一样,频道类将由 服务容器 自动解析。因此,你可以在频道类的构造函数中对所需的任何依赖进行类型提示。

广播事件

定义了一个标记有 ShouldBroadcast 接口的事件后,你只需要使用该事件的 dispatch 方法触发它即可。事件分发器会识别出该事件标记有 ShouldBroadcast 接口,并会将该事件放入队列中进行广播。

1use App\Events\OrderShipmentStatusUpdated;
2 
3OrderShipmentStatusUpdated::dispatch($order);

仅广播给其他人

在构建使用事件广播的应用时,你有时可能需要向给定频道的所有订阅者广播事件,但排除当前用户。你可以使用 broadcast 助手函数和 toOthers 方法来实现:

1use App\Events\OrderShipmentStatusUpdated;
2 
3broadcast(new OrderShipmentStatusUpdated($update))->toOthers();

为了更好地理解何时使用 toOthers 方法,让我们想象一个任务列表应用,用户可以通过输入任务名称来创建新任务。要创建任务,应用可能会向 /task URL 发送请求,该请求广播任务的创建并返回新任务的 JSON 表示。当 JavaScript 应用从端点收到响应时,它可能会直接将新任务插入到任务列表中,如下所示:

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

然而,请记住我们同时也广播了任务的创建。如果 JavaScript 应用也为了将任务添加到列表中而监听此事件,你的列表中就会出现重复的任务:一个来自端点响应,一个来自广播。你可以通过使用 toOthers 方法来解决这个问题,该方法指示广播器不要将事件广播给当前用户。

你的事件必须使用 Illuminate\Broadcasting\InteractsWithSockets trait 才能调用 toOthers 方法。

配置

当你初始化一个 Laravel Echo 实例时,会为该连接分配一个套接字 ID(socket ID)。如果你正在使用全局的 Axios 实例从 JavaScript 应用发起 HTTP 请求,套接字 ID 将自动作为 X-Socket-ID 头信息附加到每个传出请求中。然后,当你调用 toOthers 方法时,Laravel 会从头信息中提取套接字 ID,并指示广播器不要将消息广播给任何具有该套接字 ID 的连接。

如果你没有使用全局 Axios 实例,则需要手动配置 JavaScript 应用,在所有传出请求中发送 X-Socket-ID 头信息。你可以使用 Echo.socketId 方法检索该套接字 ID:

1var socketId = Echo.socketId();

自定义连接

如果你的应用与多个广播连接进行交互,并且你想使用非默认的广播器来广播事件,可以使用 via 方法指定将事件推送到哪个连接:

1use App\Events\OrderShipmentStatusUpdated;
2 
3broadcast(new OrderShipmentStatusUpdated($update))->via('pusher');

或者,你也可以通过在事件的构造函数中调用 broadcastVia 方法来指定事件的广播连接。不过,在这样做之前,请确保事件类使用了 InteractsWithBroadcasting trait:

1<?php
2 
3namespace App\Events;
4 
5use Illuminate\Broadcasting\Channel;
6use Illuminate\Broadcasting\InteractsWithBroadcasting;
7use Illuminate\Broadcasting\InteractsWithSockets;
8use Illuminate\Broadcasting\PresenceChannel;
9use Illuminate\Broadcasting\PrivateChannel;
10use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
11use Illuminate\Queue\SerializesModels;
12 
13class OrderShipmentStatusUpdated implements ShouldBroadcast
14{
15 use InteractsWithBroadcasting;
16 
17 /**
18 * Create a new event instance.
19 */
20 public function __construct()
21 {
22 $this->broadcastVia('pusher');
23 }
24}

匿名事件

有时,你可能想向应用的前端广播一个简单的事件,而无需创建专门的事件类。为了满足这一需求,Broadcast 门面(facade)允许你广播“匿名事件”:

1Broadcast::on('orders.'.$order->id)->send();

上述示例将广播以下事件:

1{
2 "event": "AnonymousEvent",
3 "data": "[]",
4 "channel": "orders.1"
5}

使用 aswith 方法,你可以自定义事件的名称和数据:

1Broadcast::on('orders.'.$order->id)
2 ->as('OrderPlaced')
3 ->with($order)
4 ->send();

上述示例将广播如下事件:

1{
2 "event": "OrderPlaced",
3 "data": "{ id: 1, total: 100 }",
4 "channel": "orders.1"
5}

如果你想在私有或 Presence 频道上广播匿名事件,可以使用 privatepresence 方法:

1Broadcast::private('orders.'.$order->id)->send();
2Broadcast::presence('channels.'.$channel->id)->send();

使用 send 方法广播匿名事件会将该事件分发到应用的 队列 进行处理。但是,如果你想立即广播该事件,可以使用 sendNow 方法:

1Broadcast::on('orders.'.$order->id)->sendNow();

若要将事件广播给所有频道订阅者,但不包括当前已认证的用户,你可以调用 toOthers 方法:

1Broadcast::on('orders.'.$order->id)
2 ->toOthers()
3 ->send();

救援广播

当应用的队列服务器不可用或者 Laravel 在广播事件时遇到错误,通常会抛出一个异常,导致终端用户看到应用错误。由于事件广播通常是对应用核心功能的补充,你可以通过在事件上实现 ShouldRescue 接口,来防止这些异常打断用户体验。

实现 ShouldRescue 接口的事件会在广播尝试期间自动利用 Laravel 的 rescue 助手函数。该助手函数会捕获任何异常,将其报告给应用的异常处理器进行日志记录,并允许应用继续正常执行,而不会打断用户的操作流程。

1<?php
2 
3namespace App\Events;
4 
5use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
6use Illuminate\Contracts\Broadcasting\ShouldRescue;
7 
8class ServerCreated implements ShouldBroadcast, ShouldRescue
9{
10 // ...
11}

接收广播

监听事件

一旦你 安装并实例化了 Laravel Echo,就可以开始监听从 Laravel 应用广播的事件了。首先,使用 channel 方法获取频道实例,然后调用 listen 方法监听指定的事件:

1Echo.channel(`orders.${this.order.id}`)
2 .listen('OrderShipmentStatusUpdated', (e) => {
3 console.log(e.order.name);
4 });

如果你想监听私有频道上的事件,请改用 private 方法。你可以继续链式调用 listen 方法,以在单个频道上监听多个事件:

1Echo.private(`orders.${this.order.id}`)
2 .listen(/* ... */)
3 .listen(/* ... */)
4 .listen(/* ... */);

停止监听事件

如果你想停止监听某个事件而不 离开频道,可以使用 stopListening 方法:

1Echo.private(`orders.${this.order.id}`)
2 .stopListening('OrderShipmentStatusUpdated');

离开频道

要离开一个频道,可以在 Echo 实例上调用 leaveChannel 方法:

1Echo.leaveChannel(`orders.${this.order.id}`);

如果你想离开一个频道及其关联的私有和 Presence 频道,可以调用 leave 方法:

1Echo.leave(`orders.${this.order.id}`);

命名空间

你可能注意到上述示例中我们没有为事件类指定完整的 App\Events 命名空间。这是因为 Echo 会自动假设事件位于 App\Events 命名空间中。但是,你可以在实例化 Echo 时通过传入 namespace 配置选项来设置根命名空间:

1window.Echo = new Echo({
2 broadcaster: 'pusher',
3 // ...
4 namespace: 'App.Other.Namespace'
5});

或者,在使用 Echo 订阅事件时,可以在事件类名称前加上 . 前缀。这将允许你始终指定完全限定的类名:

1Echo.channel('orders')
2 .listen('.Namespace\\Event\\Class', (e) => {
3 // ...
4 });

使用 React 或 Vue

Laravel Echo 包含 React 和 Vue Hooks,使得监听事件变得轻而易举。要开始使用,调用 useEcho hook,它用于监听私有事件。当所消费的组件卸载时,useEcho hook 会自动离开频道:

1import { useEcho } from "@laravel/echo-react";
2 
3useEcho(
4 `orders.${orderId}`,
5 "OrderShipmentStatusUpdated",
6 (e) => {
7 console.log(e.order);
8 },
9);
1<script setup lang="ts">
2import { useEcho } from "@laravel/echo-vue";
3 
4useEcho(
5 `orders.${orderId}`,
6 "OrderShipmentStatusUpdated",
7 (e) => {
8 console.log(e.order);
9 },
10);
11</script>

你可以通过提供一个事件数组给 useEcho 来监听多个事件:

1useEcho(
2 `orders.${orderId}`,
3 ["OrderShipmentStatusUpdated", "OrderShipped"],
4 (e) => {
5 console.log(e.order);
6 },
7);

你还可以指定广播事件载荷数据的形状(shape),从而提供更好的类型安全性和编辑便利性:

1type OrderData = {
2 order: {
3 id: number;
4 user: {
5 id: number;
6 name: string;
7 };
8 created_at: string;
9 };
10};
11 
12useEcho<OrderData>(`orders.${orderId}`, "OrderShipmentStatusUpdated", (e) => {
13 console.log(e.order.id);
14 console.log(e.order.user.id);
15});

当所消费的组件卸载时,useEcho hook 会自动离开频道;不过,你也可以利用返回的函数在必要时以编程方式手动停止/启动频道的监听:

1import { useEcho } from "@laravel/echo-react";
2 
3const { leaveChannel, leave, stopListening, listen } = useEcho(
4 `orders.${orderId}`,
5 "OrderShipmentStatusUpdated",
6 (e) => {
7 console.log(e.order);
8 },
9);
10 
11// Stop listening without leaving channel...
12stopListening();
13 
14// Start listening again...
15listen();
16 
17// Leave channel...
18leaveChannel();
19 
20// Leave a channel and also its associated private and presence channels...
21leave();
1<script setup lang="ts">
2import { useEcho } from "@laravel/echo-vue";
3 
4const { leaveChannel, leave, stopListening, listen } = useEcho(
5 `orders.${orderId}`,
6 "OrderShipmentStatusUpdated",
7 (e) => {
8 console.log(e.order);
9 },
10);
11 
12// Stop listening without leaving channel...
13stopListening();
14 
15// Start listening again...
16listen();
17 
18// Leave channel...
19leaveChannel();
20 
21// Leave a channel and also its associated private and presence channels...
22leave();
23</script>

连接到公共频道

要连接到公共频道,可以使用 useEchoPublic hook:

1import { useEchoPublic } from "@laravel/echo-react";
2 
3useEchoPublic("posts", "PostPublished", (e) => {
4 console.log(e.post);
5});
1<script setup lang="ts">
2import { useEchoPublic } from "@laravel/echo-vue";
3 
4useEchoPublic("posts", "PostPublished", (e) => {
5 console.log(e.post);
6});
7</script>

连接到 Presence 频道

要连接到 Presence 频道,可以使用 useEchoPresence hook:

1import { useEchoPresence } from "@laravel/echo-react";
2 
3useEchoPresence("posts", "PostPublished", (e) => {
4 console.log(e.post);
5});
1<script setup lang="ts">
2import { useEchoPresence } from "@laravel/echo-vue";
3 
4useEchoPresence("posts", "PostPublished", (e) => {
5 console.log(e.post);
6});
7</script>

连接状态

你可以使用 useConnectionStatus hook 获取当前 WebSocket 连接状态,它提供自动随连接状态变化而更新的响应式状态:

1import { useConnectionStatus } from "@laravel/echo-react";
2 
3function ConnectionIndicator() {
4 const status = useConnectionStatus();
5 
6 return <div>Connection: {status}</div>;
7}
1<script setup lang="ts">
2import { useConnectionStatus } from "@laravel/echo-vue";
3 
4const status = useConnectionStatus();
5</script>
6 
7<template>
8 <div>Connection: {{ status }}</div>
9</template>

可能的取值有:

  • connected - 已成功连接到 WebSocket 服务器。
  • connecting - 正在进行初始连接尝试。
  • reconnecting - 断开连接后正在尝试重新连接。
  • disconnected - 未连接且没有尝试重新连接。
  • failed - 连接失败且不会再重试。

Presence 频道(在线状态频道)

Presence 频道建立在私有频道的安全性之上,同时额外提供了感知频道内订阅者的功能。这使得构建强大的协作应用功能变得简单,例如当其他用户正在查看相同页面时通知用户,或者列出聊天室中的在线用户。

授权 Presence 频道

所有 Presence 频道也是私有频道;因此,用户必须被 授权访问。然而,在为 Presence 频道定义授权回调时,如果用户被授权加入频道,你不会返回 true,而是应该返回一个包含用户数据的数组。

授权回调返回的数据将在 JavaScript 应用的 Presence 频道事件监听器中可用。如果用户未被授权加入 Presence 频道,你应该返回 falsenull

1use App\Models\User;
2 
3Broadcast::channel('chat.{roomId}', function (User $user, int $roomId) {
4 if ($user->canJoinRoom($roomId)) {
5 return ['id' => $user->id, 'name' => $user->name];
6 }
7});

加入 Presence 频道

要加入 Presence 频道,可以使用 Echo 的 join 方法。join 方法将返回一个 PresenceChannel 实现,它除了公开 listen 方法外,还允许你订阅 herejoiningleaving 事件:

1Echo.join(`chat.${roomId}`)
2 .here((users) => {
3 // ...
4 })
5 .joining((user) => {
6 console.log(user.name);
7 })
8 .leaving((user) => {
9 console.log(user.name);
10 })
11 .error((error) => {
12 console.error(error);
13 });

here 回调会在成功加入频道后立即执行,并接收包含当前频道内所有其他已订阅用户信息的数组。joining 方法会在新用户加入频道时执行,而 leaving 方法会在用户离开频道时执行。当认证端点返回 200 以外的 HTTP 状态码或解析返回的 JSON 出现问题时,会执行 error 方法。

向 Presence 频道广播

Presence 频道可以像公共或私有频道一样接收事件。以聊天室为例,我们可能想向聊天室的 Presence 频道广播 NewMessage 事件。为此,我们将从事件的 broadcastOn 方法中返回一个 PresenceChannel 实例:

1/**
2 * Get the channels the event should broadcast on.
3 *
4 * @return array<int, \Illuminate\Broadcasting\Channel>
5 */
6public function broadcastOn(): array
7{
8 return [
9 new PresenceChannel('chat.'.$this->message->room_id),
10 ];
11}

与其他事件一样,你可以使用 broadcast 助手和 toOthers 方法来排除当前用户接收广播:

1broadcast(new NewMessage($message));
2 
3broadcast(new NewMessage($message))->toOthers();

与其他类型事件一样,你可以使用 Echo 的 listen 方法监听发送到 Presence 频道的事件:

1Echo.join(`chat.${roomId}`)
2 .here(/* ... */)
3 .joining(/* ... */)
4 .leaving(/* ... */)
5 .listen('NewMessage', (e) => {
6 // ...
7 });

模型广播

在阅读有关模型广播的以下文档之前,建议你先熟悉 Laravel 模型广播服务的一般概念,以及如何手动创建和监听广播事件。

当应用中的 Eloquent 模型 被创建、更新或删除时,通常会广播事件。当然,这可以通过手动 为 Eloquent 模型状态变更定义自定义事件 并用 ShouldBroadcast 接口标记这些事件来轻松实现。

然而,如果你的应用中没有出于其他目的使用这些事件,仅仅为了广播它们而创建事件类可能会显得繁琐。为了解决这个问题,Laravel 允许你指定 Eloquent 模型应自动广播其状态变更。

若要开始使用,你的 Eloquent 模型应使用 Illuminate\Database\Eloquent\BroadcastsEvents trait。此外,模型还应定义一个 broadcastOn 方法,该方法返回模型事件应广播到的频道数组:

1<?php
2 
3namespace App\Models;
4 
5use Illuminate\Broadcasting\Channel;
6use Illuminate\Broadcasting\PrivateChannel;
7use Illuminate\Database\Eloquent\BroadcastsEvents;
8use Illuminate\Database\Eloquent\Factories\HasFactory;
9use Illuminate\Database\Eloquent\Model;
10use Illuminate\Database\Eloquent\Relations\BelongsTo;
11 
12class Post extends Model
13{
14 use BroadcastsEvents, HasFactory;
15 
16 /**
17 * Get the user that the post belongs to.
18 */
19 public function user(): BelongsTo
20 {
21 return $this->belongsTo(User::class);
22 }
23 
24 /**
25 * Get the channels that model events should broadcast on.
26 *
27 * @return array<int, \Illuminate\Broadcasting\Channel|\Illuminate\Database\Eloquent\Model>
28 */
29 public function broadcastOn(string $event): array
30 {
31 return [$this, $this->user];
32 }
33}

一旦模型包含此 trait 并定义了广播频道,它就会在模型实例被创建、更新、删除、软删除(trashed)或恢复时自动广播事件。

此外,你可能注意到 broadcastOn 方法接收一个字符串参数 $event。该参数包含模型上发生的事件类型,值为 createdupdateddeletedtrashedrestored。通过检查该变量的值,你可以确定模型应针对特定事件广播到哪些频道(如果有的话):

1/**
2 * Get the channels that model events should broadcast on.
3 *
4 * @return array<string, array<int, \Illuminate\Broadcasting\Channel|\Illuminate\Database\Eloquent\Model>>
5 */
6public function broadcastOn(string $event): array
7{
8 return match ($event) {
9 'deleted' => [],
10 default => [$this, $this->user],
11 };
12}

自定义模型广播事件创建

有时,你可能希望自定义 Laravel 创建底层模型广播事件的方式。你可以通过在 Eloquent 模型上定义 newBroadcastableEvent 方法来实现。该方法应返回一个 Illuminate\Database\Eloquent\BroadcastableModelEventOccurred 实例:

1use Illuminate\Database\Eloquent\BroadcastableModelEventOccurred;
2 
3/**
4 * Create a new broadcastable model event for the model.
5 */
6protected function newBroadcastableEvent(string $event): BroadcastableModelEventOccurred
7{
8 return (new BroadcastableModelEventOccurred(
9 $this, $event
10 ))->dontBroadcastToCurrentUser();
11}

模型广播约定

频道约定

正如你可能注意到的,上面模型示例中的 broadcastOn 方法没有返回 Channel 实例,而是直接返回了 Eloquent 模型。如果模型的 broadcastOn 方法返回了 Eloquent 模型实例(或包含在方法返回的数组中),Laravel 将使用模型类名和主键标识符作为频道名称,自动为该模型实例化一个私有频道实例。

因此,一个 id1App\Models\User 模型将被转换为一个名为 App.Models.User.1Illuminate\Broadcasting\PrivateChannel 实例。当然,除了从模型的 broadcastOn 方法返回 Eloquent 模型实例外,你还可以返回完整的 Channel 实例,以完全控制模型的频道名称:

1use Illuminate\Broadcasting\PrivateChannel;
2 
3/**
4 * Get the channels that model events should broadcast on.
5 *
6 * @return array<int, \Illuminate\Broadcasting\Channel>
7 */
8public function broadcastOn(string $event): array
9{
10 return [
11 new PrivateChannel('user.'.$this->id)
12 ];
13}

如果你计划显式地从模型的 broadcastOn 方法返回一个频道实例,可以将 Eloquent 模型实例传递给频道的构造函数。这样,Laravel 将使用上述的模型频道约定,将 Eloquent 模型转换为频道名称字符串:

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

如果你需要确定模型的频道名称,可以在任何模型实例上调用 broadcastChannel 方法。例如,对于 id1App\Models\User 模型,此方法返回字符串 App.Models.User.1

1$user->broadcastChannel();

事件约定

由于模型广播事件并未与应用 App\Events 目录下的“实际”事件相关联,它们根据约定被赋予名称和载荷。Laravel 的约定是使用模型的类名(不包含命名空间)和触发广播的模型操作名称来广播事件。

所以,例如,对 App\Models\Post 模型的更新会作为 PostUpdated 事件广播到客户端应用,其载荷如下:

1{
2 "model": {
3 "id": 1,
4 "title": "My first post"
5 ...
6 },
7 ...
8 "socket": "someSocketId"
9}

App\Models\User 模型的删除将广播一个名为 UserDeleted 的事件。

如果你愿意,可以通过在模型中添加 broadcastAsbroadcastWith 方法来定义自定义的广播名称和载荷。这些方法接收正在发生的模型事件/操作名称,允许你为每个模型操作自定义事件名称和载荷。如果 broadcastAs 方法返回 null,Laravel 将在广播事件时使用上述的模型广播事件名称约定。

1/**
2 * The model event's broadcast name.
3 */
4public function broadcastAs(string $event): string|null
5{
6 return match ($event) {
7 'created' => 'post.created',
8 default => null,
9 };
10}
11 
12/**
13 * Get the data to broadcast for the model.
14 *
15 * @return array<string, mixed>
16 */
17public function broadcastWith(string $event): array
18{
19 return match ($event) {
20 'created' => ['title' => $this->title],
21 default => ['model' => $this],
22 };
23}

监听模型广播

一旦你将 BroadcastsEvents trait 添加到模型中并定义了 broadcastOn 方法,你就可以开始在客户端应用中监听广播的模型事件了。在开始之前,建议你查阅 监听事件 的完整文档。

首先,使用 private 方法获取频道实例,然后调用 listen 方法监听指定的事件。通常,传递给 private 方法的频道名称应符合 Laravel 的 模型广播约定

获取频道实例后,你可以使用 listen 方法监听特定事件。由于模型广播事件与应用 App\Events 目录下的“实际”事件不相关联,事件名称 前面必须加上 . 前缀,以表明它不属于特定命名空间。每个模型广播事件都有一个 model 属性,其中包含模型的所有可广播属性:

1Echo.private(`App.Models.User.${this.user.id}`)
2 .listen('.UserUpdated', (e) => {
3 console.log(e.model);
4 });

使用 React 或 Vue

如果你正在使用 React 或 Vue,可以使用 Laravel Echo 附带的 useEchoModel hook 来轻松监听模型广播:

1import { useEchoModel } from "@laravel/echo-react";
2 
3useEchoModel("App.Models.User", userId, ["UserUpdated"], (e) => {
4 console.log(e.model);
5});
1<script setup lang="ts">
2import { useEchoModel } from "@laravel/echo-vue";
3 
4useEchoModel("App.Models.User", userId, ["UserUpdated"], (e) => {
5 console.log(e.model);
6});
7</script>

你还可以指定模型事件载荷数据的形状,从而提供更好的类型安全性和编辑便利性:

1type User = {
2 id: number;
3 name: string;
4 email: string;
5};
6 
7useEchoModel<User, "App.Models.User">("App.Models.User", userId, ["UserUpdated"], (e) => {
8 console.log(e.model.id);
9 console.log(e.model.name);
10});

客户端事件

当使用 Pusher Channels 时,你必须在 应用仪表板 的“App Settings”部分启用“Client Events”选项,以便发送客户端事件。

有时你可能希望向其他已连接的客户端广播事件,而无需触及 Laravel 应用本身。这对于诸如“输入中”(typing)通知之类的事情特别有用,你希望在特定屏幕上提醒应用的其他用户某人正在输入消息。

若要广播客户端事件,可以使用 Echo 的 whisper 方法:

1Echo.private(`chat.${roomId}`)
2 .whisper('typing', {
3 name: this.user.name
4 });
1import { useEcho } from "@laravel/echo-react";
2 
3const { channel } = useEcho(`chat.${roomId}`, ['update'], (e) => {
4 console.log('Chat event received:', e);
5});
6 
7channel().whisper('typing', { name: user.name });
1<script setup lang="ts">
2import { useEcho } from "@laravel/echo-vue";
3 
4const { channel } = useEcho(`chat.${roomId}`, ['update'], (e) => {
5 console.log('Chat event received:', e);
6});
7 
8channel().whisper('typing', { name: user.name });
9</script>

要监听客户端事件,可以使用 listenForWhisper 方法:

1Echo.private(`chat.${roomId}`)
2 .listenForWhisper('typing', (e) => {
3 console.log(e.name);
4 });
1import { useEcho } from "@laravel/echo-react";
2 
3const { channel } = useEcho(`chat.${roomId}`, ['update'], (e) => {
4 console.log('Chat event received:', e);
5});
6 
7channel().listenForWhisper('typing', (e) => {
8 console.log(e.name);
9});
1<script setup lang="ts">
2import { useEcho } from "@laravel/echo-vue";
3 
4const { channel } = useEcho(`chat.${roomId}`, ['update'], (e) => {
5 console.log('Chat event received:', e);
6});
7 
8channel().listenForWhisper('typing', (e) => {
9 console.log(e.name);
10});
11</script>

通知

通过将事件广播与 通知 结合使用,JavaScript 应用可以在无需刷新页面的情况下接收新通知。在开始之前,请务必阅读关于使用 广播通知频道 的文档。

配置好通知使用广播频道后,可以使用 Echo 的 notification 方法监听广播事件。请记住,频道名称应与接收通知的实体的类名相匹配:

1Echo.private(`App.Models.User.${userId}`)
2 .notification((notification) => {
3 console.log(notification.type);
4 });
1import { useEchoModel } from "@laravel/echo-react";
2 
3const { channel } = useEchoModel('App.Models.User', userId);
4 
5channel().notification((notification) => {
6 console.log(notification.type);
7});
1<script setup lang="ts">
2import { useEchoModel } from "@laravel/echo-vue";
3 
4const { channel } = useEchoModel('App.Models.User', userId);
5 
6channel().notification((notification) => {
7 console.log(notification.type);
8});
9</script>

在此示例中,所有通过 broadcast 频道发送给 App\Models\User 实例的通知都将被回调接收。应用 routes/channels.php 文件中已包含 App.Models.User.{id} 频道的频道授权回调。

停止监听通知

如果你想停止监听通知而不 离开频道,可以使用 stopListeningForNotification 方法:

1const callback = (notification) => {
2 console.log(notification.type);
3}
4 
5// Start listening...
6Echo.private(`App.Models.User.${userId}`)
7 .notification(callback);
8 
9// Stop listening (callback must be the same)...
10Echo.private(`App.Models.User.${userId}`)
11 .stopListeningForNotification(callback);