广播
简介
在许多现代 Web 应用程序中,WebSockets 用于实现实时、实时更新的用户界面。当服务器上的某些数据更新时,通常会通过 WebSocket 连接发送消息,供客户端处理。WebSockets 提供了一种更有效的方式来替代不断轮询应用程序服务器以获取应该反映在 UI 中的数据变化。
例如,假设你的应用程序能够将用户数据导出到 CSV 文件并将其通过电子邮件发送给他们。但是,创建这个 CSV 文件需要几分钟时间,所以你选择在 排队的作业 中创建和发送 CSV。当 CSV 已创建并通过电子邮件发送给用户时,我们可以使用事件广播来调度一个 App\Events\UserDataExported
事件,该事件将被我们的应用程序的 JavaScript 接收。一旦收到该事件,我们可以向用户显示一条消息,告知他们他们的 CSV 已通过电子邮件发送给他们,而无需他们刷新页面。
为了帮助你构建这些类型的功能,Laravel 使得将你的服务器端 Laravel 事件 “广播”到 WebSocket 连接变得非常容易。广播你的 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”部分是通配符。
监听事件广播
接下来,剩下的就是监听 JavaScript 应用程序中的事件。我们可以使用 Laravel Echo 来完成此操作。首先,我们将使用 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
接口标记后,您只需要使用事件的 dispatch 方法触发事件。事件调度器将注意到事件被标记为 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 频道
存在通道建立在私有通道的安全性的基础上,同时还提供了对谁订阅了该通道的意识这一附加功能。这使得构建强大的协作应用程序功能变得容易,例如在另一个用户查看同一页面时通知用户,或者列出聊天室的成员。
授权 Presence 频道
所有存在通道也是私有通道;因此,用户必须 获得授权才能访问它们。但是,在为存在通道定义授权回调时,如果用户有权加入该通道,您将不会返回 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 频道
要加入存在通道,可以使用 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
方法将在用户离开通道时执行。error
方法将在身份验证端点返回非 200 的 HTTP 状态码或解析返回的 JSON 时出现问题时执行。
向 Presence 频道广播
存在通道可以接收事件,就像公共或私有通道一样。使用聊天室的示例,我们可能希望将 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 会自动使用模型的类名和主键标识符作为频道名称,为该模型实例化一个私有频道实例。
因此,App\Models\User
模型,其 id
为 1
,将被转换为 Illuminate\Broadcasting\PrivateChannel
实例,其名称为 App.Models.User.1
。当然,除了从您的模型的 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
方法。例如,此方法对于 App\Models\User
模型(其 id
为 1
)返回字符串 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
特性添加到您的模型并定义您的模型的 broadcastOn
方法后,您就可以开始在您的客户端应用程序中监听广播的模型事件了。在开始之前,您可能希望查阅有关 监听事件 的完整文档。
首先,使用 private
方法检索频道实例,然后调用 listen
方法来监听指定的事件。通常,传递给 private
方法的频道名称应对应于 Laravel 的 模型广播约定。
获得频道实例后,您可以使用 listen
方法来监听特定事件。由于模型广播事件与应用程序的 App\Events
目录中的“实际”事件无关,因此 事件名称 必须以 .
为前缀,以指示它不属于特定命名空间。每个模型广播事件都具有一个 model
属性,其中包含模型的所有可广播属性。
Echo.private(`App.Models.User.${this.user.id}`) .listen('.PostUpdated', (e) => { console.log(e.model); });
客户端事件
有时您可能希望将事件广播到其他连接的客户端,而根本不访问您的 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
实例的所有通知都将由回调接收。App.Models.User.{id}
频道的频道授权回调包含在您的应用程序的 routes/channels.php
文件中。