广播
简介
在许多现代 Web 应用程序中,WebSockets 用于实现实时、动态更新的用户界面。当服务器上的一些数据更新时,通常会通过 WebSocket 连接发送一条消息,由客户端处理。与持续轮询应用程序服务器以获取应反映在 UI 中的数据更改相比,WebSockets 提供了一种更高效的替代方案。
例如,假设你的应用程序能够将用户数据导出到 CSV 文件并通过电子邮件发送给用户。但是,创建此 CSV 文件需要几分钟时间,因此你选择在 队列作业 中创建和发送 CSV 文件。当 CSV 文件创建完毕并发送给用户后,我们可以使用事件广播来调度一个 App\Events\UserDataExported
事件,该事件由应用程序的 JavaScript 接收。一旦接收到事件,我们就可以向用户显示一条消息,告知他们他们的 CSV 文件已通过电子邮件发送给他们,而无需他们刷新页面。
为了帮助你构建这些类型的功能,Laravel 使得轻松地通过 WebSocket 连接“广播”你的服务器端 Laravel 事件 成为可能。广播你的 Laravel 事件允许你在服务器端 Laravel 应用程序和客户端 JavaScript 应用程序之间共享相同的事件名称和数据。
广播背后的核心概念很简单:客户端在前端连接到命名的频道,而你的 Laravel 应用程序在后端向这些频道广播事件。这些事件可以包含任何你希望提供给前端的额外数据。
支持的驱动程序
默认情况下,Laravel 包含三个服务器端广播驱动程序供你选择:Laravel Reverb、Pusher Channels 和 Ably。
在深入研究事件广播之前,请确保你已阅读 Laravel 关于 事件和监听器 的文档。
服务器端安装
要开始使用 Laravel 的事件广播,我们需要在 Laravel 应用程序中进行一些配置,并安装一些包。
事件广播由服务器端广播驱动程序完成,该驱动程序广播你的 Laravel 事件,以便 Laravel Echo(一个 JavaScript 库)可以在浏览器客户端接收它们。不用担心——我们将逐步引导你完成安装过程的每个步骤。
配置
所有应用程序的事件广播配置都存储在 config/broadcasting.php
配置文件中。如果你的应用程序中不存在此目录,请不要担心;运行 install:broadcasting
Artisan 命令时会创建它。
Laravel 原生支持多个广播驱动程序:Laravel Reverb、Pusher Channels、Ably 和用于本地开发和调试的 log
驱动程序。此外,还包含一个 null
驱动程序,允许你在测试期间禁用广播。config/broadcasting.php
配置文件中包含每个驱动程序的配置示例。
安装
默认情况下,新的 Laravel 应用程序不会启用广播。你可以使用 install:broadcasting
Artisan 命令启用广播。
php artisan install:broadcasting
install:broadcasting
命令将创建 config/broadcasting.php
配置文件。此外,该命令还将创建 routes/channels.php
文件,你可以在其中注册应用程序的广播授权路由和回调。
队列配置
在广播任何事件之前,你应该首先配置并运行 队列工作进程。所有事件广播都是通过队列作业完成的,这样应用程序的响应时间就不会受到广播事件的严重影响。
Reverb
运行 install:broadcasting
命令时,系统将提示你安装 Laravel Reverb。当然,你也可以使用 Composer 包管理器手动安装 Reverb。
composer require laravel/reverb
安装包后,你可以运行 Reverb 的安装命令来发布配置、添加 Reverb 的必需环境变量并在应用程序中启用事件广播。
php artisan reverb:install
你可以在 Reverb 文档 中找到详细的 Reverb 安装和使用说明。
Pusher Channels
如果你计划使用 Pusher Channels 广播你的事件,则应使用 Composer 包管理器安装 Pusher Channels PHP SDK。
composer require pusher/pusher-php-server
接下来,你应该在 config/broadcasting.php
配置文件中配置你的 Pusher Channels 凭据。此文件中已包含 Pusher Channels 配置示例,允许你快速指定你的密钥、密钥和应用程序 ID。通常,你应该在应用程序的 .env
文件中配置你的 Pusher Channels 凭据。
PUSHER_APP_ID="your-pusher-app-id"PUSHER_APP_KEY="your-pusher-key"PUSHER_APP_SECRET="your-pusher-secret"PUSHER_HOST=PUSHER_PORT=443PUSHER_SCHEME="https"PUSHER_APP_CLUSTER="mt1"
config/broadcasting.php
文件的 pusher
配置还允许你指定 Channels 支持的其他 options
,例如集群。
然后,在应用程序的 .env
文件中将 BROADCAST_CONNECTION
环境变量设置为 pusher
。
BROADCAST_CONNECTION=pusher
最后,你就可以安装和配置 Laravel Echo 了,它将在客户端接收广播事件。
Ably
下面的文档讨论了如何在“Pusher 兼容性”模式下使用 Ably。但是,Ably 团队推荐并维护一个广播器和 Echo 客户端,它能够利用 Ably 提供的独特功能。有关使用 Ably 维护的驱动程序的更多信息,请查阅 Ably 的 Laravel 广播器文档。
如果你计划使用 Ably 广播你的事件,则应使用 Composer 包管理器安装 Ably PHP SDK。
composer require ably/ably-php
接下来,你应该在 config/broadcasting.php
配置文件中配置你的 Ably 凭据。此文件中已包含 Ably 配置示例,允许你快速指定你的密钥。通常,应通过 ABLY_KEY
环境变量 设置此值。
ABLY_KEY=your-ably-key
然后,在应用程序的 .env
文件中将 BROADCAST_CONNECTION
环境变量设置为 ably
。
BROADCAST_CONNECTION=ably
最后,你就可以安装和配置 Laravel Echo 了,它将在客户端接收广播事件。
客户端安装
Reverb
Laravel Echo 是一个 JavaScript 库,它使订阅频道和监听服务器端广播驱动程序广播的事件变得轻而易举。你可以通过 NPM 包管理器安装 Echo。在这个例子中,我们还将安装 pusher-js
包,因为 Reverb 使用 Pusher 协议进行 WebSocket 订阅、频道和消息。
npm install --save-dev laravel-echo pusher-js
安装 Echo 后,您就可以在应用程序的 JavaScript 中创建新的 Echo 实例。一个理想的位置是 Laravel 框架包含的 `resources/js/bootstrap.js` 文件底部。默认情况下,此文件中已包含一个示例 Echo 配置 - 您只需取消注释它并将 `broadcaster` 配置选项更新为 `reverb`
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, wssPort: import.meta.env.VITE_REVERB_PORT, forceTLS: (import.meta.env.VITE_REVERB_SCHEME ?? 'https') === 'https', enabledTransports: ['ws', 'wss'],});
接下来,您应该编译应用程序的资源文件
npm run build
Laravel Echo 的 `reverb` 广播器需要 laravel-echo v1.16.0+。
Pusher Channels
Laravel Echo 是一个 JavaScript 库,它使订阅频道和监听服务器端广播驱动程序广播的事件变得轻而易举。Echo 还利用 `pusher-js` NPM 包来实现 Pusher 协议,用于 WebSocket 订阅、频道和消息。
`install:broadcasting` Artisan 命令会自动为您安装 `laravel-echo` 和 `pusher-js` 包;但是,您也可以通过 NPM 手动安装这些包。
npm install --save-dev laravel-echo pusher-js
安装 Echo 后,您就可以在应用程序的 JavaScript 中创建新的 Echo 实例。`install:broadcasting` 命令会在 `resources/js/echo.js` 中创建一个 Echo 配置文件;但是,此文件中的默认配置适用于 Laravel Reverb。您可以复制下面的配置来将您的配置转换为 Pusher
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: import.meta.env.VITE_PUSHER_APP_CLUSTER, forceTLS: true});
接下来,您应该在应用程序的 `.env` 文件中定义 Pusher 环境变量的相应值。如果您的 `.env` 文件中尚不存在这些变量,则应添加它们。
PUSHER_APP_ID="your-pusher-app-id"PUSHER_APP_KEY="your-pusher-key"PUSHER_APP_SECRET="your-pusher-secret"PUSHER_HOST=PUSHER_PORT=443PUSHER_SCHEME="https"PUSHER_APP_CLUSTER="mt1" VITE_APP_NAME="${APP_NAME}"VITE_PUSHER_APP_KEY="${PUSHER_APP_KEY}"VITE_PUSHER_HOST="${PUSHER_HOST}"VITE_PUSHER_PORT="${PUSHER_PORT}"VITE_PUSHER_SCHEME="${PUSHER_SCHEME}"VITE_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
根据您的应用程序需求调整 Echo 配置后,您可以编译应用程序的资源文件。
npm run build
要了解有关编译应用程序的 JavaScript 资源文件的更多信息,请查阅有关 Vite 的文档。
使用现有客户端实例
如果您已经有预配置的 Pusher Channels 客户端实例,希望 Echo 使用它,您可以通过 `client` 配置选项将其传递给 Echo。
import Echo from 'laravel-echo';import Pusher from 'pusher-js'; const options = { broadcaster: 'pusher', key: 'your-pusher-channels-key'} window.Echo = new Echo({ ...options, client: new Pusher(options.key, options)});
Ably
下面的文档讨论了如何在“Pusher 兼容性”模式下使用 Ably。但是,Ably 团队推荐并维护一个广播器和 Echo 客户端,它能够利用 Ably 提供的独特功能。有关使用 Ably 维护的驱动程序的更多信息,请查阅 Ably 的 Laravel 广播器文档。
Laravel Echo 是一个 JavaScript 库,它使订阅频道和监听服务器端广播驱动程序广播的事件变得轻而易举。Echo 还利用 `pusher-js` NPM 包来实现 Pusher 协议,用于 WebSocket 订阅、频道和消息。
`install:broadcasting` Artisan 命令会自动为您安装 `laravel-echo` 和 `pusher-js` 包;但是,您也可以通过 NPM 手动安装这些包。
npm install --save-dev laravel-echo pusher-js
在继续之前,您应该在 Ably 应用程序设置中启用 Pusher 协议支持。您可以在 Ably 应用程序设置控制面板的“协议适配器设置”部分启用此功能。
安装 Echo 后,您就可以在应用程序的 JavaScript 中创建新的 Echo 实例。`install:broadcasting` 命令会在 `resources/js/echo.js` 中创建一个 Echo 配置文件;但是,此文件中的默认配置适用于 Laravel Reverb。您可以复制下面的配置来将您的配置转换为 Ably
import Echo from 'laravel-echo'; import Pusher from 'pusher-js';window.Pusher = Pusher; window.Echo = new Echo({ broadcaster: 'pusher', key: import.meta.env.VITE_ABLY_PUBLIC_KEY, wsHost: 'realtime-pusher.ably.io', wsPort: 443, disableStats: true, encrypted: true,});
您可能已经注意到我们的 Ably Echo 配置引用了一个 `VITE_ABLY_PUBLIC_KEY` 环境变量。此变量的值应为您的 Ably 公钥。您的公钥是 Ably 密钥中 `:` 字符之前的部分。
根据您的需求调整 Echo 配置后,您可以编译应用程序的资源文件。
npm run dev
要了解有关编译应用程序的 JavaScript 资源文件的更多信息,请查阅有关 Vite 的文档。
概念概述
Laravel 的事件广播允许您使用基于驱动程序的 WebSocket 方法将服务器端的 Laravel 事件广播到客户端 JavaScript 应用程序。目前,Laravel 附带 Pusher Channels 和 Ably 驱动程序。可以使用 Laravel Echo JavaScript 包轻松地在客户端使用这些事件。
事件通过“频道”广播,可以指定为公共或私有。您的应用程序的任何访问者都可以订阅公共频道,无需任何身份验证或授权;但是,要订阅私有频道,用户必须经过身份验证并被授权才能收听该频道。
使用示例应用程序
在深入研究事件广播的每个组件之前,让我们使用电子商务商店为例进行高级概述。
在我们的应用程序中,假设我们有一个页面允许用户查看其订单的运输状态。还假设当应用程序处理运输状态更新时会触发 `OrderShipmentStatusUpdated` 事件。
use App\Events\OrderShipmentStatusUpdated; OrderShipmentStatusUpdated::dispatch($order);
`ShouldBroadcast` 接口
当用户查看其订单之一时,我们不希望他们必须刷新页面才能查看状态更新。相反,我们希望在创建状态更新时将其广播到应用程序。因此,我们需要使用 `ShouldBroadcast` 接口标记 `OrderShipmentStatusUpdated` 事件。这将指示 Laravel 在触发事件时广播该事件。
<?php namespace App\Events; use App\Models\Order;use Illuminate\Broadcasting\Channel;use Illuminate\Broadcasting\InteractsWithSockets;use Illuminate\Broadcasting\PresenceChannel;use Illuminate\Contracts\Broadcasting\ShouldBroadcast;use Illuminate\Queue\SerializesModels; class OrderShipmentStatusUpdated implements ShouldBroadcast{ /** * The order instance. * * @var \App\Models\Order */ public $order;}
`ShouldBroadcast` 接口要求我们的事件定义一个 `broadcastOn` 方法。此方法负责返回事件应在其上广播的频道。此方法的空存根已在生成的事件类中定义,因此我们只需要填写其详细信息。我们只希望订单创建者能够查看状态更新,因此我们将在与订单绑定的私有频道上广播事件。
use Illuminate\Broadcasting\Channel;use Illuminate\Broadcasting\PrivateChannel; /** * Get the channel the event should broadcast on. */public function broadcastOn(): Channel{ return new PrivateChannel('orders.'.$this->order->id);}
如果您希望事件在多个频道上广播,则可以返回一个 `array`。
use Illuminate\Broadcasting\PrivateChannel; /** * Get the channels the event should broadcast on. * * @return array<int, \Illuminate\Broadcasting\Channel> */public function broadcastOn(): array{ return [ new PrivateChannel('orders.'.$this->order->id), // ... ];}
授权频道
请记住,用户必须被授权才能收听私有频道。我们可以在应用程序的 `routes/channels.php` 文件中定义我们的频道授权规则。在此示例中,我们需要验证尝试收听私有 `orders.1` 频道的任何用户实际上是否是订单创建者。
use App\Models\Order;use App\Models\User; Broadcast::channel('orders.{orderId}', function (User $user, int $orderId) { return $user->id === Order::findOrNew($orderId)->user_id;});
`channel` 方法接受两个参数:频道的名称和一个回调函数,该函数返回 `true` 或 `false`,指示用户是否被授权收听该频道。
所有授权回调都将当前经过身份验证的用户作为其第一个参数,并将任何其他通配符参数作为其后续参数。在此示例中,我们使用 `{orderId}` 占位符来指示频道名称的“ID”部分是通配符。
侦听事件广播
接下来,剩下的就是使用 Laravel Echo 在我们的 JavaScript 应用程序中侦听事件。首先,我们将使用 `private` 方法订阅私有频道。然后,我们可以使用 `listen` 方法来侦听 `OrderShipmentStatusUpdated` 事件。默认情况下,事件的所有公共属性都将包含在广播事件中。
Echo.private(`orders.${orderId}`) .listen('OrderShipmentStatusUpdated', (e) => { console.log(e.order); });
定义广播事件
要告知 Laravel 应该广播给定事件,必须在事件类上实现 `Illuminate\Contracts\Broadcasting\ShouldBroadcast` 接口。此接口已导入框架生成的全部事件类中,因此您可以轻松地将其添加到任何事件中。
`ShouldBroadcast` 接口要求您实现单个方法:`broadcastOn`。`broadcastOn` 方法应返回事件应在其上广播的频道或频道数组。频道应该是 `Channel`、`PrivateChannel` 或 `PresenceChannel` 的实例。`Channel` 的实例表示任何用户都可以订阅的公共频道,而 `PrivateChannels` 和 `PresenceChannels` 表示需要 频道授权 的私有频道。
<?php namespace App\Events; use App\Models\User;use Illuminate\Broadcasting\Channel;use Illuminate\Broadcasting\InteractsWithSockets;use Illuminate\Broadcasting\PresenceChannel;use Illuminate\Broadcasting\PrivateChannel;use Illuminate\Contracts\Broadcasting\ShouldBroadcast;use Illuminate\Queue\SerializesModels; class ServerCreated implements ShouldBroadcast{ use SerializesModels; /** * Create a new event instance. */ public function __construct( public User $user, ) {} /** * Get the channels the event should broadcast on. * * @return array<int, \Illuminate\Broadcasting\Channel> */ public function broadcastOn(): array { return [ new PrivateChannel('user.'.$this->user->id), ]; }}
实现 `ShouldBroadcast` 接口后,您只需 触发事件,就像平时一样。触发事件后,排队作业 将使用您指定的广播驱动程序自动广播事件。
广播名称
默认情况下,Laravel 将使用事件的类名广播事件。但是,您可以通过在事件上定义 `broadcastAs` 方法来自定义广播名称。
/** * The event's broadcast name. */public function broadcastAs(): string{ return 'server.created';}
如果您使用 `broadcastAs` 方法自定义广播名称,则应确保使用前导 `.` 字符注册侦听器。这将指示 Echo 不要将应用程序的命名空间添加到事件的前面。
.listen('.server.created', function (e) { ....});
广播数据
广播事件时,其所有 `public` 属性都会自动序列化并作为事件的有效负载广播,允许您从 JavaScript 应用程序访问其任何公共数据。例如,如果您的事件具有包含 Eloquent 模型的单个公共 `$user` 属性,则事件的广播有效负载将是
{ "user": { "id": 1, "name": "Patrick Stewart" ... }}
但是,如果您希望更精细地控制广播有效负载,则可以在事件中添加 `broadcastWith` 方法。此方法应返回您希望作为事件有效负载广播的数据数组。
/** * Get the data to broadcast. * * @return array<string, mixed> */public function broadcastWith(): array{ return ['id' => $this->user->id];}
广播队列
默认情况下,每个广播事件都放在 `queue.php` 配置文件中指定的默认队列连接的默认队列中。您可以通过在事件类上定义 `connection` 和 `queue` 属性来自定义广播器使用的队列连接和名称。
/** * The name of the queue connection to use when broadcasting the event. * * @var string */public $connection = 'redis'; /** * The name of the queue on which to place the broadcasting job. * * @var string */public $queue = 'default';
或者,您可以通过在事件上定义 `broadcastQueue` 方法来自定义队列名称。
/** * The name of the queue on which to place the broadcasting job. */public function broadcastQueue(): string{ return 'default';}
如果您想使用 `sync` 队列而不是默认队列驱动程序来广播事件,则可以实现 `ShouldBroadcastNow` 接口而不是 `ShouldBroadcast`。
<?php use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow; class OrderShipmentStatusUpdated implements ShouldBroadcastNow{ // ...}
广播条件
有时您只想在给定条件为真时才广播事件。您可以通过在事件类中添加 `broadcastWhen` 方法来定义这些条件。
/** * Determine if this event should broadcast. */public function broadcastWhen(): bool{ return $this->order->value > 100;}
广播和数据库事务
在数据库事务中调度广播事件时,它们可能会在数据库事务提交之前由队列处理。发生这种情况时,您在数据库事务期间对模型或数据库记录所做的任何更新可能尚未反映在数据库中。此外,在事务中创建的任何模型或数据库记录可能不存在于数据库中。如果您的事件依赖于这些模型,则在处理广播事件的作业时可能会发生意外错误。
如果队列连接的 `after_commit` 配置选项设置为 `false`,您仍然可以通过在事件类上实现 `ShouldDispatchAfterCommit` 接口来指示应在所有打开的数据库事务提交后调度特定的广播事件。
<?php namespace App\Events; use Illuminate\Contracts\Broadcasting\ShouldBroadcast;use Illuminate\Contracts\Events\ShouldDispatchAfterCommit;use Illuminate\Queue\SerializesModels; class ServerCreated implements ShouldBroadcast, ShouldDispatchAfterCommit{ use SerializesModels;}
要了解有关解决这些问题的更多信息,请查看有关 排队作业和数据库事务 的文档。
授权频道
私有频道要求您授权当前已认证的用户实际上可以监听该频道。这是通过向您的 Laravel 应用程序发送包含频道名称的 HTTP 请求,并允许您的应用程序确定用户是否可以监听该频道来实现的。当使用Laravel Echo时,授权订阅私有频道的 HTTP 请求将自动发出。
启用广播后,Laravel 会自动注册/broadcasting/auth
路由来处理授权请求。/broadcasting/auth
路由自动放置在web
中间件组中。
定义授权回调
接下来,我们需要定义实际确定当前已认证的用户是否可以监听给定频道的逻辑。这在install:broadcasting
Artisan 命令创建的routes/channels.php
文件中完成。在此文件中,您可以使用Broadcast::channel
方法注册频道授权回调。
use App\Models\User; Broadcast::channel('orders.{orderId}', function (User $user, int $orderId) { return $user->id === Order::findOrNew($orderId)->user_id;});
`channel` 方法接受两个参数:频道的名称和一个回调函数,该函数返回 `true` 或 `false`,指示用户是否被授权收听该频道。
所有授权回调都将当前经过身份验证的用户作为其第一个参数,并将任何其他通配符参数作为其后续参数。在此示例中,我们使用 `{orderId}` 占位符来指示频道名称的“ID”部分是通配符。
您可以使用channel:list
Artisan 命令查看应用程序广播授权回调列表。
php artisan channel:list
授权回调模型绑定
就像 HTTP 路由一样,频道路由也可以利用隐式和显式路由模型绑定。例如,您可以请求实际的Order
模型实例,而不是接收字符串或数字订单 ID。
use App\Models\Order;use App\Models\User; Broadcast::channel('orders.{order}', function (User $user, Order $order) { return $user->id === $order->user_id;});
与 HTTP 路由模型绑定不同,频道模型绑定不支持自动隐式模型绑定作用域。但是,这很少是一个问题,因为大多数频道可以根据单个模型的唯一主键进行作用域。
授权回调身份验证
私有和存在广播频道通过应用程序的默认身份验证守卫对当前用户进行身份验证。如果用户未进行身份验证,则会自动拒绝频道授权,并且永远不会执行授权回调。但是,您可以分配多个自定义守卫,如有必要,这些守卫应验证传入的请求。
Broadcast::channel('channel', function () { // ...}, ['guards' => ['web', 'admin']]);
定义频道类
如果您的应用程序正在使用许多不同的频道,您的routes/channels.php
文件可能会变得很庞大。因此,您可以使用频道类,而不是使用闭包来授权频道。要生成频道类,请使用make:channel
Artisan 命令。此命令会将新的频道类放置在App/Broadcasting
目录中。
php artisan make:channel OrderChannel
接下来,在您的routes/channels.php
文件中注册您的频道。
use App\Broadcasting\OrderChannel; Broadcast::channel('orders.{order}', OrderChannel::class);
最后,您可以将频道授权逻辑放在频道类的join
方法中。此join
方法将包含您通常放在频道授权闭包中的相同逻辑。您还可以利用频道模型绑定。
<?php namespace App\Broadcasting; use App\Models\Order;use App\Models\User; class OrderChannel{ /** * Create a new channel instance. */ public function __construct() {} /** * Authenticate the user's access to the channel. */ public function join(User $user, Order $order): array|bool { return $user->id === $order->user_id; }}
与 Laravel 中许多其他类一样,频道类将由服务容器自动解析。因此,您可以在其构造函数中为频道所需的任何依赖项指定类型提示。
广播事件
定义事件并用ShouldBroadcast
接口标记后,您只需使用事件的调度方法来触发事件。事件调度程序会注意到该事件用ShouldBroadcast
接口标记,并将该事件排队以进行广播。
use App\Events\OrderShipmentStatusUpdated; OrderShipmentStatusUpdated::dispatch($order);
仅限其他人
在构建使用事件广播的应用程序时,您可能偶尔需要将事件广播给给定频道的除当前用户以外的所有订阅者。您可以使用broadcast
帮助程序和toOthers
方法来实现此目的。
use App\Events\OrderShipmentStatusUpdated; broadcast(new OrderShipmentStatusUpdated($update))->toOthers();
为了更好地理解何时可能需要使用toOthers
方法,让我们想象一个任务列表应用程序,其中用户可以通过输入任务名称来创建新任务。要创建任务,您的应用程序可能会向/task
URL 发送请求,该请求会广播任务的创建并返回新任务的 JSON 表示形式。当您的 JavaScript 应用程序从端点接收响应时,它可能会像这样直接将新任务插入到其任务列表中。
axios.post('/task', task) .then((response) => { this.tasks.push(response.data); });
但是,请记住我们还广播了任务的创建。如果您的 JavaScript 应用程序还在侦听此事件以便将任务添加到任务列表中,则您的列表中将有重复的任务:一个来自端点,一个来自广播。您可以通过使用toOthers
方法来指示广播器不要将事件广播给当前用户来解决此问题。
您的事件必须使用Illuminate\Broadcasting\InteractsWithSockets
特性才能调用toOthers
方法。
配置
初始化 Laravel Echo 实例时,会为连接分配一个套接字 ID。如果您使用全局Axios实例从您的 JavaScript 应用程序发出 HTTP 请求,则套接字 ID 将自动作为X-Socket-ID
标头附加到每个传出请求。然后,当您调用toOthers
方法时,Laravel 将从标头中提取套接字 ID 并指示广播器不要广播到具有该套接字 ID 的任何连接。
如果您不使用全局 Axios 实例,则需要手动配置您的 JavaScript 应用程序以使用所有传出请求发送X-Socket-ID
标头。您可以使用Echo.socketId
方法检索套接字 ID。
var socketId = Echo.socketId();
自定义连接
如果您的应用程序与多个广播连接交互,并且您想使用默认广播器以外的广播器广播事件,您可以使用via
方法指定要将事件推送到哪个连接。
use App\Events\OrderShipmentStatusUpdated; broadcast(new OrderShipmentStatusUpdated($update))->via('pusher');
或者,您可以在事件的构造函数中调用broadcastVia
方法来指定事件的广播连接。但是,在此之前,您应该确保事件类使用了InteractsWithBroadcasting
特性。
<?php namespace App\Events; use Illuminate\Broadcasting\Channel;use Illuminate\Broadcasting\InteractsWithBroadcasting;use Illuminate\Broadcasting\InteractsWithSockets;use Illuminate\Broadcasting\PresenceChannel;use Illuminate\Broadcasting\PrivateChannel;use Illuminate\Contracts\Broadcasting\ShouldBroadcast;use Illuminate\Queue\SerializesModels; class OrderShipmentStatusUpdated implements ShouldBroadcast{ use InteractsWithBroadcasting; /** * Create a new event instance. */ public function __construct() { $this->broadcastVia('pusher'); }}
匿名事件
有时,您可能希望将简单的事件广播到应用程序的前端,而无需创建专用的事件类。为了适应这一点,Broadcast
外观允许您广播“匿名事件”。
Broadcast::on('orders.'.$order->id)->send();
上面的示例将广播以下事件:
{ "event": "AnonymousEvent", "data": "[]", "channel": "orders.1"}
使用as
和with
方法,您可以自定义事件的名称和数据。
Broadcast::on('orders.'.$order->id) ->as('OrderPlaced') ->with($order) ->send();
上面的示例将广播类似于以下内容的事件:
{ "event": "OrderPlaced", "data": "{ id: 1, total: 100 }", "channel": "orders.1"}
如果您想在私有或存在频道上广播匿名事件,您可以使用private
和presence
方法。
Broadcast::private('orders.'.$order->id)->send();Broadcast::presence('channels.'.$channel->id)->send();
使用send
方法广播匿名事件会将事件调度到应用程序的队列以进行处理。但是,如果您想立即广播事件,可以使用sendNow
方法。
Broadcast::on('orders.'.$order->id)->sendNow();
要将事件广播给除当前已认证用户以外的所有频道订阅者,您可以调用toOthers
方法。
Broadcast::on('orders.'.$order->id) ->toOthers() ->send();
接收广播
监听事件
一旦您安装并实例化了 Laravel Echo,您就可以开始侦听从您的 Laravel 应用程序广播的事件了。首先,使用channel
方法检索频道实例,然后调用listen
方法来侦听指定的事件。
Echo.channel(`orders.${this.order.id}`) .listen('OrderShipmentStatusUpdated', (e) => { console.log(e.order.name); });
如果您想侦听私有频道上的事件,请改用private
方法。您可以继续将调用链接到listen
方法以侦听单个频道上的多个事件。
Echo.private(`orders.${this.order.id}`) .listen(/* ... */) .listen(/* ... */) .listen(/* ... */);
停止侦听事件
如果您想停止侦听给定事件而不离开频道,可以使用stopListening
方法。
Echo.private(`orders.${this.order.id}`) .stopListening('OrderShipmentStatusUpdated')
离开频道
要离开频道,您可以在 Echo 实例上调用leaveChannel
方法。
Echo.leaveChannel(`orders.${this.order.id}`);
如果您想离开频道及其关联的私有和存在频道,您可以调用leave
方法。
Echo.leave(`orders.${this.order.id}`);
命名空间
您可能在上面的示例中注意到,我们没有为事件类指定完整的App\Events
命名空间。这是因为 Echo 将自动假设事件位于App\Events
命名空间中。但是,您可以在实例化 Echo 时通过传递namespace
配置选项来配置根命名空间。
window.Echo = new Echo({ broadcaster: 'pusher', // ... namespace: 'App.Other.Namespace'});
或者,您可以在使用 Echo 订阅事件时在事件类前加上.
。这将允许您始终指定完全限定的类名。
Echo.channel('orders') .listen('.Namespace\\Event\\Class', (e) => { // ... });
Presence Channels (存在频道)
存在频道建立在私有频道的安全性的基础上,同时还提供了了解谁订阅了该频道的额外功能。这使得构建强大的协作应用程序功能变得容易,例如在另一个用户查看同一页面时通知用户或列出聊天室的成员。
授权 Presence Channels
所有存在频道也是私有频道;因此,必须授权用户访问它们。但是,在为存在频道定义授权回调时,如果用户有权加入频道,您将不会返回true
。相反,您应该返回关于用户的数据数组。
授权回调返回的数据将在您的 JavaScript 应用程序中的存在频道事件侦听器中可用。如果用户无权加入存在频道,则应返回false
或null
。
use App\Models\User; Broadcast::channel('chat.{roomId}', function (User $user, int $roomId) { if ($user->canJoinRoom($roomId)) { return ['id' => $user->id, 'name' => $user->name]; }});
加入 Presence Channels
要加入存在频道,您可以使用 Echo 的join
方法。join
方法将返回一个PresenceChannel
实现,该实现除了公开listen
方法外,还允许您订阅here
、joining
和leaving
事件。
Echo.join(`chat.${roomId}`) .here((users) => { // ... }) .joining((user) => { console.log(user.name); }) .leaving((user) => { console.log(user.name); }) .error((error) => { console.error(error); });
成功加入频道后,将立即执行here
回调,并将接收一个包含当前订阅该频道的所有其他用户的用户信息的数组。当新用户加入频道时,将执行joining
方法,而当用户离开频道时,将执行leaving
方法。当身份验证端点返回的 HTTP 状态代码不是 200 或解析返回的 JSON 时出现问题时,将执行error
方法。
向 Presence Channels 广播
存在频道可以像公共或私有频道一样接收事件。以聊天室为例,我们可能希望将NewMessage
事件广播到房间的存在频道。为此,我们将从事件的broadcastOn
方法返回一个PresenceChannel
实例。
/** * Get the channels the event should broadcast on. * * @return array<int, \Illuminate\Broadcasting\Channel> */public function broadcastOn(): array{ return [ new PresenceChannel('chat.'.$this->message->room_id), ];}
与其他事件一样,您可以使用broadcast
帮助程序和toOthers
方法来排除当前用户接收广播。
broadcast(new NewMessage($message)); broadcast(new NewMessage($message))->toOthers();
与其他类型的事件一样,您可以使用 Echo 的listen
方法侦听发送到存在频道的事件。
Echo.join(`chat.${roomId}`) .here(/* ... */) .joining(/* ... */) .leaving(/* ... */) .listen('NewMessage', (e) => { // ... });
模型广播
在阅读以下关于模型广播的文档之前,我们建议您熟悉 Laravel 模型广播服务的通用概念以及如何手动创建和侦听广播事件。
当应用程序的Eloquent 模型创建、更新或删除时,广播事件是很常见的。当然,这可以通过手动为 Eloquent 模型状态更改定义自定义事件并使用ShouldBroadcast
接口标记这些事件来轻松实现。
但是,如果您没有在应用程序中将这些事件用于任何其他目的,则仅仅为了广播而创建事件类可能会很麻烦。为了解决这个问题,Laravel 允许您指示 Eloquent 模型应该自动广播其状态更改。
要开始,您的 Eloquent 模型应该使用Illuminate\Database\Eloquent\BroadcastsEvents
特性。此外,模型还应该定义一个broadcastOn
方法,该方法将返回模型事件应该在其上广播的频道数组。
<?php namespace App\Models; use Illuminate\Broadcasting\Channel;use Illuminate\Broadcasting\PrivateChannel;use Illuminate\Database\Eloquent\BroadcastsEvents;use Illuminate\Database\Eloquent\Factories\HasFactory;use Illuminate\Database\Eloquent\Model;use Illuminate\Database\Eloquent\Relations\BelongsTo; class Post extends Model{ use BroadcastsEvents, HasFactory; /** * Get the user that the post belongs to. */ public function user(): BelongsTo { return $this->belongsTo(User::class); } /** * Get the channels that model events should broadcast on. * * @return array<int, \Illuminate\Broadcasting\Channel|\Illuminate\Database\Eloquent\Model> */ public function broadcastOn(string $event): array { return [$this, $this->user]; }}
一旦您的模型包含此特性并定义了其广播频道,它将在创建、更新、删除、放入回收站或恢复模型实例时开始自动广播事件。
此外,您可能已经注意到broadcastOn
方法接收一个字符串$event
参数。此参数包含在模型上发生的事件类型,其值将为created
、updated
、deleted
、trashed
或restored
。通过检查此变量的值,您可以确定模型应该为特定事件广播到哪些频道(如果有)。
/** * Get the channels that model events should broadcast on. * * @return array<string, array<int, \Illuminate\Broadcasting\Channel|\Illuminate\Database\Eloquent\Model>> */public function broadcastOn(string $event): array{ return match ($event) { 'deleted' => [], default => [$this, $this->user], };}
自定义模型广播事件创建
有时,您可能希望自定义 Laravel 如何创建底层的模型广播事件。您可以通过在 Eloquent 模型上定义一个 `newBroadcastableEvent` 方法来实现这一点。此方法应返回一个 `Illuminate\Database\Eloquent\BroadcastableModelEventOccurred` 实例。
use Illuminate\Database\Eloquent\BroadcastableModelEventOccurred; /** * Create a new broadcastable model event for the model. */protected function newBroadcastableEvent(string $event): BroadcastableModelEventOccurred{ return (new BroadcastableModelEventOccurred( $this, $event ))->dontBroadcastToCurrentUser();}
模型广播约定
频道约定
您可能已经注意到,上面模型示例中的 `broadcastOn` 方法没有返回 `Channel` 实例。而是直接返回 Eloquent 模型。如果您的模型的 `broadcastOn` 方法返回 Eloquent 模型实例(或包含在该方法返回的数组中),Laravel 将使用模型的类名和主键标识符作为频道名称自动实例化一个私有频道实例。
因此,一个 `id` 为 `1` 的 `App\Models\User` 模型将被转换为一个名为 `App.Models.User.1` 的 `Illuminate\Broadcasting\PrivateChannel` 实例。当然,除了从模型的 `broadcastOn` 方法返回 Eloquent 模型实例外,您还可以返回完整的 `Channel` 实例,以便完全控制模型的频道名称。
use Illuminate\Broadcasting\PrivateChannel; /** * Get the channels that model events should broadcast on. * * @return array<int, \Illuminate\Broadcasting\Channel> */public function broadcastOn(string $event): array{ return [ new PrivateChannel('user.'.$this->id) ];}
如果您计划从模型的 `broadcastOn` 方法显式返回一个频道实例,您可以将 Eloquent 模型实例传递给频道的构造函数。这样做时,Laravel 将使用上面讨论的模型频道约定将 Eloquent 模型转换为频道名称字符串。
return [new Channel($this->user)];
如果您需要确定模型的频道名称,可以在任何模型实例上调用 `broadcastChannel` 方法。例如,对于 `id` 为 `1` 的 `App\Models\User` 模型,此方法返回字符串 `App.Models.User.1`。
$user->broadcastChannel()
事件约定
由于模型广播事件与应用程序 `App\Events` 目录中的“实际”事件无关,因此它们基于约定分配名称和有效负载。Laravel 的约定是使用模型的类名(不包括命名空间)和触发广播的模型事件的名称来广播事件。
例如,对 `App\Models\Post` 模型的更新将向您的客户端应用程序广播一个名为 `PostUpdated` 的事件,并带有以下有效负载。
{ "model": { "id": 1, "title": "My first post" ... }, ... "socket": "someSocketId",}
删除 `App\Models\User` 模型将广播一个名为 `UserDeleted` 的事件。
如果您愿意,可以通过向模型添加 `broadcastAs` 和 `broadcastWith` 方法来定义自定义广播名称和有效负载。这些方法接收正在发生的模型事件/操作的名称,允许您为每个模型操作自定义事件的名称和有效负载。如果 `broadcastAs` 方法返回 `null`,Laravel 将在广播事件时使用上面讨论的模型广播事件名称约定。
/** * The model event's broadcast name. */public function broadcastAs(string $event): string|null{ return match ($event) { 'created' => 'post.created', default => null, };} /** * Get the data to broadcast for the model. * * @return array<string, mixed> */public function broadcastWith(string $event): array{ return match ($event) { 'created' => ['title' => $this->title], default => ['model' => $this], };}
监听模型广播
将 `BroadcastsEvents` trait 添加到您的模型并定义模型的 `broadcastOn` 方法后,您就可以开始在客户端应用程序中侦听广播的模型事件了。在开始之前,您可能需要查阅关于侦听事件的完整文档。
首先,使用 `private` 方法检索频道实例,然后调用 `listen` 方法来侦听指定的事件。通常,提供给 `private` 方法的频道名称应与 Laravel 的模型广播约定相对应。
获得频道实例后,您可以使用 `listen` 方法侦听特定事件。由于模型广播事件与应用程序 `App\Events` 目录中的“实际”事件无关,因此事件名称必须以 `.` 为前缀,以指示它不属于特定命名空间。每个模型广播事件都有一个 `model` 属性,其中包含模型的所有可广播属性。
Echo.private(`App.Models.User.${this.user.id}`) .listen('.PostUpdated', (e) => { console.log(e.model); });
客户端事件
当使用Pusher Channels时,您必须在应用程序仪表板的“应用程序设置”部分启用“客户端事件”选项才能发送客户端事件。
有时,您可能希望将事件广播给其他连接的客户端,而根本不访问您的 Laravel 应用程序。这对于诸如“键入”通知之类的事件特别有用,在这种情况下,您希望提醒应用程序的用户,另一个用户正在给定的屏幕上键入消息。
要广播客户端事件,您可以使用 Echo 的 `whisper` 方法。
Echo.private(`chat.${roomId}`) .whisper('typing', { name: this.user.name });
要侦听客户端事件,您可以使用 `listenForWhisper` 方法。
Echo.private(`chat.${roomId}`) .listenForWhisper('typing', (e) => { console.log(e.name); });
通知
通过将事件广播与通知结合使用,您的 JavaScript 应用程序可以在通知发生时接收新通知,而无需刷新页面。在开始之前,请务必阅读有关使用广播通知频道的文档。
将通知配置为使用广播频道后,您可以使用 Echo 的 `notification` 方法侦听广播事件。请记住,频道名称应与接收通知的实体的类名匹配。
Echo.private(`App.Models.User.${userId}`) .notification((notification) => { console.log(notification.type); });
在此示例中,通过 `broadcast` 频道发送到 `App\Models\User` 实例的所有通知都将由回调接收。您的应用程序的 `routes/channels.php` 文件中包含 `App.Models.User.{id}` 频道的频道授权回调。