广播
简介
在许多现代 Web 应用程序中,WebSocket 用于实现实时、实时更新的用户界面。当服务器上的某些数据更新时,通常会通过 WebSocket 连接发送一条消息以供客户端处理。 WebSocket 提供了一种更有效的替代方案,可以持续轮询应用程序服务器以获取应反映在 UI 中的数据更改。
例如,假设你的应用程序能够将用户的数据导出到 CSV 文件并通过电子邮件发送给他们。但是,创建此 CSV 文件需要几分钟时间,因此你选择在 queued job 内创建并邮寄 CSV。创建 CSV 并将其邮寄给用户后,我们可以使用事件广播来调度应用程序的 JavaScript 接收的 App\Events\UserDataExported 事件。收到事件后,我们可以向用户显示一条消息,表明他们的 CSV 已通过电子邮件发送给他们,而无需他们刷新页面。
为了帮助你构建这些类型的功能,Laravel 可以轻松地通过 WebSocket 连接「广播」服务器端 Laravel events。广播 Laravel 事件允许你在服务器端 Laravel 应用程序和客户端 JavaScript 应用程序之间共享相同的事件名称和数据。
广播背后的核心概念很简单:客户端连接到前端的命名通道,而 Laravel 应用程序将事件广播到后端的这些通道。这些事件可以包含你希望提供给前端的任何附加数据。
支持的驱动程序
默认情况下,Laravel 包含三个服务器端广播驱动程序供你选择:Laravel Reverb、Pusher Channels 和 Ably。
INFO
在深入了解事件广播之前,请确保你已阅读 Laravel 关于 events and listeners 的文档。
服务器端安装
要开始使用 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:broadcastinginstall:broadcasting 命令会创建 config/broadcasting.php 配置文件。此外,该命令还会创建 routes/channels.php 文件,你可以在其中注册应用程序的广播授权路由与回调。
队列配置
在广播任何事件之前,你应该首先配置并运行 queue worker。所有事件广播都是通过排队作业完成的,因此应用程序的响应时间不会受到广播事件的严重影响。
Reverb
运行install:broadcasting命令时,系统会提示你安装Laravel Reverb。当然,你也可以使用 Composer 包管理器手动安装 Reverb:
composer require laravel/reverb安装软件包后,你可以运行 Reverb 的安装命令来发布配置,添加 Reverb 所需的环境变量,并在应用程序中启用事件广播:
php artisan reverb:install你可以在 Reverb documentation 中找到详细的 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=443
PUSHER_SCHEME="https"
PUSHER_APP_CLUSTER="mt1"config/broadcasting.php 文件的 pusher 配置还允许你指定通道支持的其他 options,例如集群。
然后,在应用程序的 .env 文件中将 BROADCAST_CONNECTION 环境变量设置为 pusher:
BROADCAST_CONNECTION=pusher最后,你准备好安装和配置Laravel Echo,它将在客户端接收广播事件。
Ably
INFO
下面的文档讨论了如何在「Pusher 兼容性」模式下使用 Ably。然而,Ablly 团队推荐并维护一个能够利用 Ably 提供的独特功能的广播公司和 Echo 客户端。有关使用 Ably 维护的驱动程序的更多信息,请consult Ably's Laravel broadcaster documentation。
若计划使用 Ably 广播事件,应使用 Composer 包管理器安装 Ably PHP SDK:
composer require ably/ably-php接下来,你应该在 config/broadcasting.php 配置文件中配置你的 Ably 凭据。此文件中已包含示例 Ably 配置,使你可以快速指定密钥。通常,该值应通过 ABLY_KEY environment variable 设置:
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 buildWARNING
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=443
PUSHER_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 buildINFO
要了解有关编译应用程序 JavaScript 资源的更多信息,请参阅 Vite 上的文档。
使用现有客户端实例
如果你已经有一个希望 Echo 使用的预配置 Pusher Channels 客户端实例,你可以通过 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
INFO
下面的文档讨论了如何在「Pusher 兼容性」模式下使用 Ably。然而,Ablly 团队推荐并维护一个能够利用 Ably 提供的独特功能的广播公司和 Echo 客户端。有关使用 Ably 维护的驱动程序的更多信息,请consult Ably's Laravel broadcaster documentation。
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 devINFO
要了解有关编译应用程序 JavaScript 资源的更多信息,请参阅 Vite 上的文档。
概念概述
Laravel 的事件广播允许你使用基于驱动程序的 WebSocket 方法将服务器端 Laravel 事件广播到客户端 JavaScript 应用程序。目前,Laravel 附带 Laravel Reverb、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 代表需要 channel authorization 的私人频道:
<?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 接口后,你只需像平常一样 fire the event 即可。事件触发后,queued job 将使用你指定的广播驱动程序自动广播该事件。
广播名称
默认情况下,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;
}INFO
要了解有关解决这些问题的更多信息,请查看有关 queued jobs and database transactions 的文档。
授权渠道
私人频道需要你授权当前经过身份验证的用户可以实际收听该频道。这是通过使用通道名称向 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 路由一样,通道路由也可以利用隐式和显式的 route model binding。例如,你可以请求实际的 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;
});
WARNING
与 HTTP 路由模型绑定不同,通道模型绑定不支持自动 implicit model binding scoping。然而,这很少是一个问题,因为大多数通道都可以根据单个模型的唯一主键来确定范围。
授权回调认证
私有和存在广播通道通过应用程序的默认身份验证防护对当前用户进行身份验证。如果用户未通过身份验证,通道授权将自动被拒绝,并且授权回调永远不会执行。但是,你可以分配多个自定义防护,以在必要时对传入请求进行身份验证:
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;
}
}INFO
与 Laravel 中的许多其他类一样,通道类将自动由 service container 解析。因此,你可以在通道的构造函数中键入提示所需的任何依赖项。
广播活动
一旦定义了一个事件并用 ShouldBroadcast 接口标记它,你只需使用该事件的调度方法来触发该事件。事件调度程序将注意到该事件被标记为 ShouldBroadcast 接口,并将该事件排队以进行广播:
use App\Events\OrderShipmentStatusUpdated;
OrderShipmentStatusUpdated::dispatch($order);
仅限其他人
在构建使用事件广播的应用程序时,你有时可能需要向给定频道的所有订阅者广播事件(当前用户除外)。你可以使用 broadcast 帮助器和 toOthers 方法来完成此操作:
use App\Events\OrderShipmentStatusUpdated;
broadcast(new OrderShipmentStatusUpdated($update))->toOthers();
为了更好地理解何时需要使用 toOthers 方法,让我们想象一个任务列表应用程序,用户可以通过输入任务名称来创建新任务。要创建任务,你的应用程序可能会向 /task URL 发出请求,该 URL 会广播任务的创建并返回新任务的 JSON 表示形式。当你的 JavaScript 应用程序收到来自端点的响应时,它可能会直接将新任务插入到其任务列表中,如下所示:
axios.post('/task', task)
.then((response) => {
this.tasks.push(response.data);
});但是,请记住,我们还广播了任务的创建。如果你的 JavaScript 应用程序也在侦听此事件以便将任务添加到任务列表中,则你的列表中将会有重复的任务:一项来自端点,一项来自广播。你可以通过使用 toOthers 方法指示广播者不要向当前用户广播该事件来解决此问题。
WARNING
你的事件必须使用 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 方法广播匿名事件会将事件分派到应用程序的 queue 进行处理。但是,如果你想立即广播该事件,你可以使用 sendNow 方法:
Broadcast::on('orders.'.$order->id)->sendNow();要将事件广播给除当前经过身份验证的用户之外的所有频道订阅者,你可以调用 toOthers 方法:
Broadcast::on('orders.'.$order->id)
->toOthers()
->send();接收广播
监听事件
一旦你有了 installed and instantiated 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(/* ... */);停止监听事件
如果你想在没有 leaving the channel 的情况下停止监听给定事件,你可以使用 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) => {
// ...
});存在渠道
存在频道建立在私人频道的安全性之上,同时还提供了了解频道订阅者的附加功能。这使得构建强大的协作应用程序功能变得很容易,例如当另一个用户正在查看同一页面时通知用户或列出聊天室的成员。
授权存在通道
所有呈现频道也是私人频道;因此,用户必须是 authorized to access them。但是,在为状态频道定义授权回调时,如果用户被授权加入频道,则不会返回 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];
}
});
加入状态频道
要加入状态频道,你可以使用 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 方法。当身份验证端点返回 200 以外的 HTTP 状态代码或解析返回的 JSON 时出现问题时,将执行 error 方法。
广播到现场频道
状态通道可以像公共或私人通道一样接收事件。以聊天室为例,我们可能希望将 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) => {
// ...
});模范广播
WARNING
在阅读以下有关模型广播的文档之前,我们建议你熟悉 Laravel 模型广播服务的一般概念以及如何手动创建和监听广播事件。
创建、更新或删除应用程序的 Eloquent models 时广播事件是很常见的。当然,这可以通过手动 defining custom events for Eloquent model state changes 并使用 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 方法。例如,此方法返回 App\Models\User 模型的字符串 App.Models.User.1,其 id 为 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 方法后,你就可以开始在客户端应用程序中侦听广播的模型事件了。在开始之前,你可能希望查阅有关 listening for events 的完整文档。
首先,使用 private 方法检索通道的实例,然后调用 listen 方法侦听指定事件。通常,为 private 方法指定的通道名称应与 Laravel 的 model broadcasting conventions 相对应。
获得通道实例后,你可以使用 listen 方法来监听特定事件。由于模型广播事件与应用程序的 App\Events 目录中的「实际」事件无关,因此 event name 必须以 . 为前缀,以表明它不属于特定命名空间。每个模型广播事件都有一个 model 属性,其中包含模型的所有可广播属性:
Echo.private(`App.Models.User.${this.user.id}`)
.listen('.PostUpdated', (e) => {
console.log(e.model);
});客户活动
INFO
使用 Pusher Channels 时,你必须在 application dashboard 的「应用设置」部分启用「客户端事件」选项才能发送客户端事件。
有时,你可能希望向其他连接的客户端广播事件,而根本不需要访问你的 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);
});通知
通过将事件广播与 notifications 配对,你的 JavaScript 应用程序可以在新通知发生时收到新通知,而无需刷新页面。在开始之前,请务必阅读有关使用 the broadcast notification channel 的文档。
配置通知以使用广播通道后,你可以使用 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 文件中。