翻译进度
33
分块数量
1
参与人数

广播系统

这是一篇协同翻译的文章,你可以点击『我来翻译』按钮来参与翻译。


广播

简介

在许多现代 Web 应用中,WebSocket 被用来实现实时、动态更新的用户界面。当服务器上的某些数据发生更新时,通常会通过 WebSocket 连接发送一条消息,并由客户端进行处理。相比持续轮询应用服务器来检查需要反映到 UI 中的数据变化,WebSocket 提供了一种更加高效的替代方案。
例如,假设你的应用可以将用户数据导出为 CSV 文件,并通过电子邮件发送给用户。但是,生成这个 CSV 文件需要几分钟时间,因此你决定通过一个 队列任务 来生成并发送 CSV。当 CSV 文件生成完成并发送给用户后,我们可以使用事件广播来触发一个 App\Events\UserDataExported 事件,并由应用中的 JavaScript 接收该事件。一旦接收到这个事件,我们就可以向用户显示一条消息,告知他们 CSV 文件已经通过电子邮件发送,而不需要用户手动刷新页面。

无与伦比 翻译于 1周前

为了帮助你构建这类功能,Laravel 可以轻松地通过 WebSocket 连接“广播”服务端的 Laravel 事件。通过广播 Laravel 事件,你可以在服务端 Laravel 应用和客户端 JavaScript 应用之间共享相同的事件名称和数据。

广播背后的核心概念很简单:客户端在前端连接到指定名称的频道,而 Laravel 应用则在后端向这些频道广播事件。这些事件可以包含任何你希望提供给前端使用的附加数据。

支持的驱动

默认情况下,Laravel 提供了三种服务端广播驱动供你选择:Laravel Reverb、Pusher Channels 和 Ably。

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

快速开始

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

php artisan install:broadcasting

install:broadcasting 命令会提示你选择要使用的事件广播服务。此外,它还会创建 config/broadcasting.php 配置文件以及 routes/channels.php 文件,你可以在其中注册应用的广播授权路由和回调。

Laravel 开箱即用地支持多种广播驱动:Laravel Reverb、Pusher Channels、Ably,以及用于本地开发和调试的 log 驱动。

此外,Laravel 还提供了一个 null 驱动,可以让你在测试期间禁用广播。config/broadcasting.php 配置文件中包含了这些驱动各自的配置示例。

无与伦比 翻译于 1周前

应用中所有与事件广播相关的配置都存储在 config/broadcasting.php 配置文件中。如果你的应用中还不存在这个文件,也不用担心;当你运行 install:broadcasting Artisan 命令时,它会自动创建。

下一步

启用事件广播后,你就可以继续了解如何 定义广播事件 和 监听事件。
如果你正在使用 Laravel 的 React、Vue 或 Svelte 入门套件,你可以通过 Echo 提供的 useEcho Hook 来监听事件。

[!注意]
在广播任何事件之前,你应该先配置并运行一个 队列 Worker。所有事件广播都会通过队列任务执行,这样可以避免事件广播对应用的响应时间造成明显影响。

服务端安装

要开始使用 Laravel 的事件广播功能,我们需要先在 Laravel 应用中完成一些配置,并安装几个相关的软件包。
事件广播是通过服务端的广播驱动实现的。该驱动会广播 Laravel 事件,以便 Laravel Echo(一个 JavaScript 库)能够在浏览器客户端中接收这些事件。不用担心,下面我们会一步一步介绍整个安装过程。

Reverb

如果你希望使用 Reverb 作为事件广播服务,可以通过执行带有 --reverb 选项的 install:broadcasting Artisan 命令,快速启用 Laravel 的广播功能。
这个 Artisan 命令会安装 Reverb 所需的 Composer 和 NPM 软件包,并在应用的 .env 文件中添加或更新相应的环境变量:

php artisan install:broadcasting --reverb
无与伦比 翻译于 1周前

手动安装

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

composer require laravel/reverb

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

php artisan reverb:install

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

Pusher Channels

如果你希望使用 Pusher 作为事件广播服务,可以通过执行带有 --pusher 选项的 install:broadcasting Artisan 命令,快速启用 Laravel 的广播功能。

这个 Artisan 命令会提示你输入 Pusher 凭据,安装 Pusher 的 PHP 和 JavaScript SDK,并在应用的 .env 文件中添加或更新相应的环境变量:

php artisan install:broadcasting --pusher

手动安装

如果要手动安装 Pusher 支持,你需要使用 Composer 包管理器安装 Pusher Channels PHP SDK:

composer require pusher/pusher-php-server

接下来,你需要在 config/broadcasting.php 配置文件中配置 Pusher Channels 的凭据。
该文件中已经包含了一个 Pusher Channels 的配置示例,你可以快速配置你的 key、secret 和 application 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"
无与伦比 翻译于 1周前

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

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

BROADCAST_CONNECTION=pusher

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

Ably

[!NOTE]
下面的文档介绍的是如何以“Pusher 兼容模式”使用 Ably。不过,Ably 团队推荐并维护了专门的广播驱动和 Echo 客户端,可以充分利用 Ably 提供的独特功能。有关如何使用 Ably 官方维护驱动的更多信息,请参阅 Ably 的 Laravel broadcaster 文档。

如果你希望使用 Ably 作为事件广播服务,可以通过执行带有 --ably 选项的 install:broadcasting Artisan 命令,快速启用 Laravel 的广播功能。

这个 Artisan 命令会提示你输入 Ably 凭据,安装 Ably 的 PHP 和 JavaScript SDK,并在应用的 .env 文件中添加或更新相应的环境变量:

php artisan install:broadcasting --ably

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

手动安装

如果要手动安装 Ably 支持,你需要使用 Composer 包管理器安装 Ably PHP SDK:

composer require ably/ably-php

接下来,你需要在 config/broadcasting.php 配置文件中配置 Ably 凭据。

该文件中已经包含了一个 Ably 配置示例,你可以快速设置你的 key。通常,这个值应该通过 ABLY_KEY 环境变量 进行设置:

ABLY_KEY=your-ably-key
无与伦比 翻译于 1周前

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

BROADCAST_CONNECTION=ably

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

客户端安装

Reverb

Laravel Echo 是一个 JavaScript 库,可以让你非常方便地订阅频道,并监听由服务端广播驱动发送的事件。

当你通过 install:broadcasting Artisan 命令安装 Laravel Reverb 时,Reverb 和 Echo 所需的脚手架代码及配置会自动添加到你的应用中。

不过,如果你希望手动配置 Laravel Echo,也可以按照下面的说明进行操作。

手动安装

如果要为应用前端手动配置 Laravel Echo,首先需要安装 pusher-js 包,因为 Reverb 使用 Pusher 协议来处理 WebSocket 订阅、频道和消息:

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

安装 Echo 后,就可以在应用的 JavaScript 中创建一个新的 Echo 实例。
一个比较合适的位置是在 Laravel 框架默认提供的 resources/js/app.js 文件底部:

import Echo from 'laravel-echo';

import Pusher from 'pusher-js';
window.Pusher = Pusher;

window.Echo = new Echo({
    broadcaster: 'reverb',
    key: import.meta.env.VITE_REVERB_APP_KEY,
    wsHost: import.meta.env.VITE_REVERB_HOST,
    wsPort: import.meta.env.VITE_REVERB_PORT ?? 80,
    wssPort: import.meta.env.VITE_REVERB_PORT ?? 443,
    forceTLS: (import.meta.env.VITE_REVERB_SCHEME ?? 'https') === 'https',
    enabledTransports: ['ws', 'wss'],
});
import { configureEcho } from "@laravel/echo-react";

configureEcho({
    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'],
});
import { configureEcho } from "@laravel/echo-vue";

configureEcho({
    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'],
});
import { configureEcho } from "@laravel/echo-svelte";

configureEcho({
    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'],
});
无与伦比 翻译于 1周前

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

npm 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-echo 和 pusher-js 软件包。它们使用 Pusher 协议来处理 WebSocket 订阅、频道和消息:

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

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

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
});
import { configureEcho } from "@laravel/echo-react";

configureEcho({
    broadcaster: "pusher",
    // key: import.meta.env.VITE_PUSHER_APP_KEY,
    // cluster: import.meta.env.VITE_PUSHER_APP_CLUSTER,
    // forceTLS: true,
    // wsHost: import.meta.env.VITE_PUSHER_HOST,
    // wsPort: import.meta.env.VITE_PUSHER_PORT,
    // wssPort: import.meta.env.VITE_PUSHER_PORT,
    // enabledTransports: ["ws", "wss"],
});
import { configureEcho } from "@laravel/echo-vue";

configureEcho({
    broadcaster: "pusher",
    // key: import.meta.env.VITE_PUSHER_APP_KEY,
    // cluster: import.meta.env.VITE_PUSHER_APP_CLUSTER,
    // forceTLS: true,
    // wsHost: import.meta.env.VITE_PUSHER_HOST,
    // wsPort: import.meta.env.VITE_PUSHER_PORT,
    // wssPort: import.meta.env.VITE_PUSHER_PORT,
    // enabledTransports: ["ws", "wss"],
});
import { configureEcho } from "@laravel/echo-svelte";

configureEcho({
    broadcaster: "pusher",
    // key: import.meta.env.VITE_PUSHER_APP_KEY,
    // cluster: import.meta.env.VITE_PUSHER_APP_CLUSTER,
    // forceTLS: true,
    // wsHost: import.meta.env.VITE_PUSHER_HOST,
    // wsPort: import.meta.env.VITE_PUSHER_PORT,
    // wssPort: import.meta.env.VITE_PUSHER_PORT,
    // enabledTransports: ["ws", "wss"],
});
无与伦比 翻译于 1周前

接下来,你需要在应用的 .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 build

[!注意]
如果你想进一步了解如何编译应用的 JavaScript 资源,请参阅 Vite 文档。

使用现有的客户端实例

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

import Echo from 'laravel-echo';
import Pusher from 'pusher-js';

const options = {
    broadcaster: 'pusher',
    key: import.meta.env.VITE_PUSHER_APP_KEY
}

window.Echo = new Echo({
    ...options,
    client: new Pusher(options.key, options)
});

Ably

[!注意]
下面的文档介绍的是如何以“Pusher 兼容模式”使用 Ably。不过,Ably 团队推荐并维护了一套专门的广播驱动和 Echo 客户端,可以充分利用 Ably 提供的独特功能。有关如何使用 Ably 官方维护驱动的更多信息,请参阅 Ably 的 Laravel broadcaster 文档。

无与伦比 翻译于 1周前

Laravel Echo 是一个 JavaScript 库,可以让你轻松订阅频道,并监听由服务端广播驱动发送的事件。

当你通过 install:broadcasting --ably Artisan 命令安装广播支持时,Ably 和 Echo 所需的脚手架代码及配置会自动添加到你的应用中。

不过,如果你希望手动配置 Laravel Echo,也可以按照下面的说明进行操作。

手动安装

如果要为应用前端手动配置 Laravel Echo,首先需要安装 laravel-echo 和 pusher-js 软件包。它们使用 Pusher 协议来处理 WebSocket 订阅、频道和消息:

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

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

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

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,
});
import { configureEcho } from "@laravel/echo-react";

configureEcho({
    broadcaster: "ably",
    // key: import.meta.env.VITE_ABLY_PUBLIC_KEY,
    // wsHost: "realtime-pusher.ably.io",
    // wsPort: 443,
    // disableStats: true,
    // encrypted: true,
});
import { configureEcho } from "@laravel/echo-vue";

configureEcho({
    broadcaster: "ably",
    // key: import.meta.env.VITE_ABLY_PUBLIC_KEY,
    // wsHost: "realtime-pusher.ably.io",
    // wsPort: 443,
    // disableStats: true,
    // encrypted: true,
});
import { configureEcho } from "@laravel/echo-svelte";

configureEcho({
    broadcaster: "ably",
    // key: import.meta.env.VITE_ABLY_PUBLIC_KEY,
    // wsHost: "realtime-pusher.ably.io",
    // wsPort: 443,
    // disableStats: true,
    // encrypted: true,
});
无与伦比 翻译于 1周前

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

根据你的需要调整好 Echo 配置后,就可以编译应用的前端资源:

npm run dev

[!注意]
如果你想进一步了解如何编译应用的 JavaScript 资源,请参阅 Vite 文档。

概念概览

Laravel 的事件广播允许你通过基于驱动的 WebSocket 方式,将服务端 Laravel 事件广播到客户端 JavaScript 应用。

目前,Laravel 内置支持 Laravel Reverb、Pusher Channels 和 Ably 驱动。这些事件可以通过客户端的 Laravel Echo JavaScript 包轻松接收和处理。

事件是通过“频道(channels)”进行广播的,这些频道可以设置为公共频道或私有频道。

任何访问你应用的用户都可以订阅公共频道,而不需要进行任何身份认证或授权;但是,如果要订阅私有频道,用户必须先通过身份认证,并且获得监听该频道的授权。

使用示例应用

在深入了解事件广播的各个组成部分之前,我们先以一个电商应用为例,从整体上了解一下事件广播的工作方式。

假设我们的应用中有一个页面,允许用户查看自己订单的配送状态。

同时假设,当应用处理订单配送状态更新时,会触发一个 OrderShipmentStatusUpdated 事件:

use App\Events\OrderShipmentStatusUpdated;

OrderShipmentStatusUpdated::dispatch($order);
无与伦比 翻译于 1周前

ShouldBroadcast 接口

当用户正在查看自己的某个订单时,我们不希望他们为了查看状态更新而手动刷新页面。相反,我们希望这些更新在产生时就自动广播到应用中。

因此,我们需要让 OrderShipmentStatusUpdated 事件实现 ShouldBroadcast 接口。这样,当事件被触发时,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
{
    /**
     * 订单实例。
     *
     * @var \App\Models\Order
     */
    public $order;
}

ShouldBroadcast 接口要求事件必须定义一个 broadcastOn 方法。这个方法负责返回该事件应该广播到哪些频道。在通过 Laravel 生成的事件类中,通常已经包含了这个方法的空实现,因此我们只需要补充具体逻辑即可。由于我们只希望订单的创建者能够查看订单状态更新,因此这里会将事件广播到一个与该订单绑定的私有频道:

use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\PrivateChannel;

/**
 * 获取事件应该广播到的频道。
 */
public function broadcastOn(): Channel
{
    return new PrivateChannel('orders.'.$this->order->id);
}

如果你希望事件同时广播到多个频道,也可以返回一个 array:

use Illuminate\Broadcasting\PrivateChannel;

/**
 * 获取事件应该广播到的频道。
 *
 * @return array<int, \Illuminate\Broadcasting\Channel>
 */
public function broadcastOn(): array
{
    return [
        new PrivateChannel('orders.'.$this->order->id),
        // ...
    ];
}
无与伦比 翻译于 1周前

授权频道

请记住,用户必须经过授权后才能监听私有频道。我们可以在应用的 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 来完成。Laravel Echo 内置的 React、Vue 和 Svelte Hooks 可以让你非常方便地开始监听事件。默认情况下,事件中的所有公共属性都会包含在广播事件中:

import { useEcho } from "@laravel/echo-react";

useEcho(
    `orders.${orderId}`,
    "OrderShipmentStatusUpdated",
    (e) => {
        console.log(e.order);
    },
);
<script setup lang="ts">
import { useEcho } from "@laravel/echo-vue";

useEcho(
    `orders.${orderId}`,
    "OrderShipmentStatusUpdated",
    (e) => {
        console.log(e.order);
    },
);
</script>
<script>
import { useEcho } from "@laravel/echo-svelte";

useEcho(
    `orders.${orderId}`,
    "OrderShipmentStatusUpdated",
    (e) => {
        console.log(e.order);
    },
);
</script>
无与伦比 翻译于 1周前

定义广播事件

要告诉 Laravel 某个事件需要被广播,你必须让该事件类实现 Illuminate\Contracts\Broadcasting\ShouldBroadcast 接口。
Laravel 框架生成的所有事件类中都已经导入了这个接口,因此你可以很方便地将它添加到任何事件中。ShouldBroadcast 接口要求你实现一个方法:broadcastOn。broadcastOn 方法应该返回一个频道,或者返回一个频道数组,用来指定该事件应该广播到哪些频道。这些频道应该是 Channel、PrivateChannel 或 PresenceChannel 的实例。Channel 实例表示公共频道,任何用户都可以订阅;而 PrivateChannel 和 PresenceChannel 表示私有频道,需要进行 频道授权:

<?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;

    /**
     * 创建一个新的事件实例。
     */
    public function __construct(
        public User $user,
    ) {}

    /**
     * 获取事件应该广播到的频道。
     *
     * @return array<int, \Illuminate\Broadcasting\Channel>
     */
    public function broadcastOn(): array
    {
        return [
            new PrivateChannel('user.'.$this->user->id),
        ];
    }
}

实现 ShouldBroadcast 接口之后,你只需要像平时一样 触发事件 即可。事件触发后,Laravel 会自动创建一个 队列任务,并使用你配置的广播驱动来广播该事件。

广播名称

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

/**
 * 事件的广播名称。
 */
public function broadcastAs(): string
{
    return 'server.created';
}
无与伦比 翻译于 1周前

如果你使用 broadcastAs 方法自定义了广播名称,那么在注册监听器时,需要确保事件名称前面带有一个 . 字符。这样可以告诉 Echo 不要在事件名称前自动添加应用的命名空间:

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

广播数据

当一个事件被广播时,它所有的 public 属性都会被自动序列化,并作为事件的 payload 进行广播。因此,你可以在 JavaScript 应用中访问这些公开数据。例如,如果你的事件中只有一个公开的 $user 属性,并且该属性包含一个 Eloquent 模型,那么事件广播的 payload 大致如下:

{
    "user": {
        "id": 1,
        "name": "Patrick Stewart"
        ...
    }
}

不过,如果你希望更精细地控制广播的 payload,可以在事件类中添加一个 broadcastWith 方法。
这个方法应该返回一个数组,其中包含你希望作为事件 payload 广播的数据:

/**
 * 获取需要广播的数据。
 *
 * @return array<string, mixed>
 */
public function broadcastWith(): array
{
    return ['id' => $this->user->id];
}

广播队列

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

use Illuminate\Queue\Attributes\Connection;
use Illuminate\Queue\Attributes\Queue;

#[Connection('redis')]
#[Queue('default')]
class ServerCreated implements ShouldBroadcast
{
    // ...
}

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

/**
 * 用于放置广播任务的队列名称。
 */
public function broadcastQueue(): string
{
    return 'default';
}
无与伦比 翻译于 1周前

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

<?php

namespace App\Events;

use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;

class OrderShipmentStatusUpdated implements ShouldBroadcastNow
{
    // ...
}

广播条件

有时,你可能只希望在某个条件成立时才广播事件。你可以通过在事件类中添加 broadcastWhen 方法来定义这个条件:

/**
 * 判断该事件是否应该广播。
 */
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;
}

[!注意]
如果你想进一步了解如何处理这类问题,请参阅有关 队列任务与数据库事务 的文档。

无与伦比 翻译于 1周前

Authorizing Channels

Private channels require you to authorize that the currently authenticated user can actually listen on the channel. This is accomplished by making an HTTP request to your Laravel application with the channel name and allowing your application to determine if the user can listen on that channel. When using Laravel Echo, the HTTP request to authorize subscriptions to private channels will be made automatically.

When broadcasting is installed Laravel attempts to automatically register the /broadcasting/auth route to handle authorization requests. If Laravel fails to automatically register these routes, you may register them manually in your application's /bootstrap/app.php file:

->withRouting(
    web: __DIR__.'/../routes/web.php',
    channels: __DIR__.'/../routes/channels.php',
    health: '/up',
)

Defining Authorization Callbacks

Next, we need to define the logic that will actually determine if the currently authenticated user can listen to a given channel. This is done in the routes/channels.php file that was created by the install:broadcasting Artisan command. In this file, you may use the Broadcast::channel method to register channel authorization callbacks:

use App\Models\User;

Broadcast::channel('orders.{orderId}', function (User $user, int $orderId) {
    return $user->id === Order::findOrNew($orderId)->user_id;
});

The channel method accepts two arguments: the name of the channel and a callback which returns true or false indicating whether the user is authorized to listen on the channel.

All authorization callbacks receive the currently authenticated user as their first argument and any additional wildcard parameters as their subsequent arguments. In this example, we are using the {orderId} placeholder to indicate that the "ID" portion of the channel name is a wildcard.

You may view a list of your application's broadcast authorization callbacks using the channel:list Artisan command:

php artisan channel:list

Authorization Callback Model Binding

Just like HTTP routes, channel routes may also take advantage of implicit and explicit route model binding. For example, instead of receiving a string or numeric order ID, you may request an actual Order model instance:

use App\Models\Order;
use App\Models\User;

Broadcast::channel('orders.{order}', function (User $user, Order $order) {
    return $user->id === $order->user_id;
});

[!WARNING]
Unlike HTTP route model binding, channel model binding does not support automatic implicit model binding scoping. However, this is rarely a problem because most channels can be scoped based on a single model's unique, primary key.

Authorization Callback Authentication

Private and presence broadcast channels authenticate the current user via your application's default authentication guard. If the user is not authenticated, channel authorization is automatically denied and the authorization callback is never executed. However, you may assign multiple, custom guards that should authenticate the incoming request if necessary:

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

Defining Channel Classes

If your application is consuming many different channels, your routes/channels.php file could become bulky. So, instead of using closures to authorize channels, you may use channel classes. To generate a channel class, use the make:channel Artisan command. This command will place a new channel class in the App/Broadcasting directory.

php artisan make:channel OrderChannel

Next, register your channel in your routes/channels.php file:

use App\Broadcasting\OrderChannel;

Broadcast::channel('orders.{order}', OrderChannel::class);

Finally, you may place the authorization logic for your channel in the channel class' join method. This join method will house the same logic you would have typically placed in your channel authorization closure. You may also take advantage of channel model binding:

<?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;
    }
}

[!NOTE]
Like many other classes in Laravel, channel classes will automatically be resolved by the service container. So, you may type-hint any dependencies required by your channel in its constructor.

Broadcasting Events

Once you have defined an event and marked it with the ShouldBroadcast interface, you only need to fire the event using the event's dispatch method. The event dispatcher will notice that the event is marked with the ShouldBroadcast interface and will queue the event for broadcasting:

use App\Events\OrderShipmentStatusUpdated;

OrderShipmentStatusUpdated::dispatch($order);

Only to Others

When building an application that utilizes event broadcasting, you may occasionally need to broadcast an event to all subscribers to a given channel except for the current user. You may accomplish this using the broadcast helper and the toOthers method:

use App\Events\OrderShipmentStatusUpdated;

broadcast(new OrderShipmentStatusUpdated($update))->toOthers();

To better understand when you may want to use the toOthers method, let's imagine a task list application where a user may create a new task by entering a task name. To create a task, your application might make a request to a /task URL which broadcasts the task's creation and returns a JSON representation of the new task. When your JavaScript application receives the response from the end-point, it might directly insert the new task into its task list like so:

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

However, remember that we also broadcast the task's creation. If your JavaScript application is also listening for this event in order to add tasks to the task list, you will have duplicate tasks in your list: one from the end-point and one from the broadcast. You may solve this by using the toOthers method to instruct the broadcaster to not broadcast the event to the current user.

[!WARNING]
Your event must use the Illuminate\Broadcasting\InteractsWithSockets trait in order to call the toOthers method.

Configuration

When you initialize a Laravel Echo instance, a socket ID is assigned to the connection. If you are using a global Axios instance to make HTTP requests from your JavaScript application, the socket ID will automatically be attached to every outgoing request as an X-Socket-ID header. Then, when you call the toOthers method, Laravel will extract the socket ID from the header and instruct the broadcaster to not broadcast to any connections with that socket ID.

If you are not using a global Axios instance, you will need to manually configure your JavaScript application to send the X-Socket-ID header with all outgoing requests. You may retrieve the socket ID using the Echo.socketId method:

var socketId = Echo.socketId();

Customizing the Connection

If your application interacts with multiple broadcast connections and you want to broadcast an event using a broadcaster other than your default, you may specify which connection to push an event to using the via method:

use App\Events\OrderShipmentStatusUpdated;

broadcast(new OrderShipmentStatusUpdated($update))->via('pusher');

Alternatively, you may specify the event's broadcast connection by calling the broadcastVia method within the event's constructor. However, before doing so, you should ensure that the event class uses the InteractsWithBroadcasting trait:

<?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');
    }
}

Anonymous Events

Sometimes, you may want to broadcast a simple event to your application's frontend without creating a dedicated event class. To accommodate this, the Broadcast facade allows you to broadcast "anonymous events":

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

The example above will broadcast the following event:

{
    "event": "AnonymousEvent",
    "data": "[]",
    "channel": "orders.1"
}

Using the as and with methods, you may customize the event's name and data:

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

The example above will broadcast an event like the following:

{
    "event": "OrderPlaced",
    "data": "{ id: 1, total: 100 }",
    "channel": "orders.1"
}

If you would like to broadcast the anonymous event on a private or presence channel, you may utilize the private and presence methods:

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

Broadcasting an anonymous event using the send method dispatches the event to your application's queue for processing. However, if you would like to broadcast the event immediately, you may use the sendNow method:

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

To broadcast the event to all channel subscribers except the currently authenticated user, you can invoke the toOthers method:

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

Rescuing Broadcasts

When your application's queue server is unavailable or Laravel encounters an error while broadcasting an event, an exception is thrown that typically causes the end user to see an application error. Since event broadcasting is often supplementary to your application's core functionality, you can prevent these exceptions from disrupting the user experience by implementing the ShouldRescue interface on your events.

Events that implement the ShouldRescue interface automatically utilize Laravel's rescue helper function during broadcast attempts. This helper catches any exceptions, reports them to your application's exception handler for logging, and allows the application to continue executing normally without interrupting the user's workflow:

<?php

namespace App\Events;

use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Contracts\Broadcasting\ShouldRescue;

class ServerCreated implements ShouldBroadcast, ShouldRescue
{
    // ...
}

Receiving Broadcasts

Listening for Events

Once you have installed and instantiated Laravel Echo, you are ready to start listening for events that are broadcast from your Laravel application. First, use the channel method to retrieve an instance of a channel, then call the listen method to listen for a specified event:

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

If you would like to listen for events on a private channel, use the private method instead. You may continue to chain calls to the listen method to listen for multiple events on a single channel:

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

Stop Listening for Events

If you would like to stop listening to a given event without leaving the channel, you may use the stopListening method:

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

Leaving a Channel

To leave a channel, you may call the leaveChannel method on your Echo instance:

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

If you would like to leave a channel and also its associated private and presence channels, you may call the leave method:

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

Namespaces

You may have noticed in the examples above that we did not specify the full App\Events namespace for the event classes. This is because Echo will automatically assume the events are located in the App\Events namespace. However, you may configure the root namespace when you instantiate Echo by passing a namespace configuration option:

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

Alternatively, you may prefix event classes with a . when subscribing to them using Echo. This will allow you to always specify the fully-qualified class name:

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

Using React, Vue, or Svelte

Laravel Echo includes React, Vue, and Svelte hooks that make it painless to listen for events. To get started, invoke the useEcho hook, which is used to listen for private events. The useEcho hook will automatically leave channels when the consuming component is unmounted:

import { useEcho } from "@laravel/echo-react";

useEcho(
    `orders.${orderId}`,
    "OrderShipmentStatusUpdated",
    (e) => {
        console.log(e.order);
    },
);
<script setup lang="ts">
import { useEcho } from "@laravel/echo-vue";

useEcho(
    `orders.${orderId}`,
    "OrderShipmentStatusUpdated",
    (e) => {
        console.log(e.order);
    },
);
</script>
<script>
import { useEcho } from "@laravel/echo-svelte";

useEcho(
    `orders.${orderId}`,
    "OrderShipmentStatusUpdated",
    (e) => {
        console.log(e.order);
    },
);
</script>

You may listen to multiple events by providing an array of events to useEcho:

useEcho(
    `orders.${orderId}`,
    ["OrderShipmentStatusUpdated", "OrderShipped"],
    (e) => {
        console.log(e.order);
    },
);

You may also specify the shape of the broadcast event payload data, providing greater type safety and editing convenience:

type OrderData = {
    order: {
        id: number;
        user: {
            id: number;
            name: string;
        };
        created_at: string;
    };
};

useEcho<OrderData>(`orders.${orderId}`, "OrderShipmentStatusUpdated", (e) => {
    console.log(e.order.id);
    console.log(e.order.user.id);
});

The useEcho hook will automatically leave channels when the consuming component is unmounted; however, you may utilize the returned functions to manually stop / start listening to channels programmatically when necessary:

import { useEcho } from "@laravel/echo-react";

const { leaveChannel, leave, stopListening, listen } = useEcho(
    `orders.${orderId}`,
    "OrderShipmentStatusUpdated",
    (e) => {
        console.log(e.order);
    },
);

// Stop listening without leaving channel...
stopListening();

// Start listening again...
listen();

// Leave channel...
leaveChannel();

// Leave a channel and also its associated private and presence channels...
leave();
<script setup lang="ts">
import { useEcho } from "@laravel/echo-vue";

const { leaveChannel, leave, stopListening, listen } = useEcho(
    `orders.${orderId}`,
    "OrderShipmentStatusUpdated",
    (e) => {
        console.log(e.order);
    },
);

// Stop listening without leaving channel...
stopListening();

// Start listening again...
listen();

// Leave channel...
leaveChannel();

// Leave a channel and also its associated private and presence channels...
leave();
</script>
<script>
import { useEcho } from "@laravel/echo-svelte";

const { leaveChannel, leave, stopListening, listen } = useEcho(
    `orders.${orderId}`,
    "OrderShipmentStatusUpdated",
    (e) => {
        console.log(e.order);
    },
);

// Stop listening without leaving channel...
stopListening();

// Start listening again...
listen();

// Leave channel...
leaveChannel();

// Leave a channel and also its associated private and presence channels...
leave();
</script>

Connecting to Public Channels

To connect to a public channel, you may use the useEchoPublic hook:

import { useEchoPublic } from "@laravel/echo-react";

useEchoPublic("posts", "PostPublished", (e) => {
    console.log(e.post);
});
<script setup lang="ts">
import { useEchoPublic } from "@laravel/echo-vue";

useEchoPublic("posts", "PostPublished", (e) => {
    console.log(e.post);
});
</script>
<script>
import { useEchoPublic } from "@laravel/echo-svelte";

useEchoPublic("posts", "PostPublished", (e) => {
    console.log(e.post);
});
</script>

Connecting to Presence Channels

To connect to a presence channel, you may use the useEchoPresence hook:

import { useEchoPresence } from "@laravel/echo-react";

useEchoPresence("posts", "PostPublished", (e) => {
    console.log(e.post);
});
<script setup lang="ts">
import { useEchoPresence } from "@laravel/echo-vue";

useEchoPresence("posts", "PostPublished", (e) => {
    console.log(e.post);
});
</script>
<script>
import { useEchoPresence } from "@laravel/echo-svelte";

useEchoPresence("posts", "PostPublished", (e) => {
    console.log(e.post);
});
</script>

Connection Status

You may retrieve the current WebSocket connection status using the useConnectionStatus hook, which provides reactive status that automatically updates when the connection state changes:

import { useConnectionStatus } from "@laravel/echo-react";

function ConnectionIndicator() {
    const status = useConnectionStatus();

    return <div>Connection: {status}</div>;
}
<script setup lang="ts">
import { useConnectionStatus } from "@laravel/echo-vue";

const status = useConnectionStatus();
</script>

<template>
    <div>Connection: {{ status }}</div>
</template>
<script>
import { useConnectionStatus } from "@laravel/echo-svelte";

const status = useConnectionStatus();
</script>

<div>Connection: {status()}</div>

The possible status values are:

  • connected - Successfully connected to the WebSocket server.
  • connecting - Initial connection attempt in progress.
  • reconnecting - Attempting to reconnect after a disconnection.
  • disconnected - Not connected and not attempting to reconnect.
  • failed - Connection failed and won't retry.

Presence Channels

Presence channels build on the security of private channels while exposing the additional feature of awareness of who is subscribed to the channel. This makes it easy to build powerful, collaborative application features such as notifying users when another user is viewing the same page or listing the inhabitants of a chat room.

Authorizing Presence Channels

All presence channels are also private channels; therefore, users must be authorized to access them. However, when defining authorization callbacks for presence channels, you will not return true if the user is authorized to join the channel. Instead, you should return an array of data about the user.

The data returned by the authorization callback will be made available to the presence channel event listeners in your JavaScript application. If the user is not authorized to join the presence channel, you should return false or 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];
    }
});

Joining Presence Channels

To join a presence channel, you may use Echo's join method. The join method will return a PresenceChannel implementation which, along with exposing the listen method, allows you to subscribe to the here, joining, and leaving events.

Echo.join(`chat.${roomId}`)
    .here((users) => {
        // ...
    })
    .joining((user) => {
        console.log(user.name);
    })
    .leaving((user) => {
        console.log(user.name);
    })
    .error((error) => {
        console.error(error);
    });

The here callback will be executed immediately once the channel is joined successfully, and will receive an array containing the user information for all of the other users currently subscribed to the channel. The joining method will be executed when a new user joins a channel, while the leaving method will be executed when a user leaves the channel. The error method will be executed when the authentication endpoint returns an HTTP status code other than 200 or if there is a problem parsing the returned JSON.

Broadcasting to Presence Channels

Presence channels may receive events just like public or private channels. Using the example of a chatroom, we may want to broadcast NewMessage events to the room's presence channel. To do so, we'll return an instance of PresenceChannel from the event's broadcastOn method:

/**
 * 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),
    ];
}

As with other events, you may use the broadcast helper and the toOthers method to exclude the current user from receiving the broadcast:

broadcast(new NewMessage($message));

broadcast(new NewMessage($message))->toOthers();

As typical of other types of events, you may listen for events sent to presence channels using Echo's listen method:

Echo.join(`chat.${roomId}`)
    .here(/* ... */)
    .joining(/* ... */)
    .leaving(/* ... */)
    .listen('NewMessage', (e) => {
        // ...
    });

Model Broadcasting

[!WARNING]
Before reading the following documentation about model broadcasting, we recommend you become familiar with the general concepts of Laravel's model broadcasting services as well as how to manually create and listen to broadcast events.

It is common to broadcast events when your application's Eloquent models are created, updated, or deleted. Of course, this can easily be accomplished by manually defining custom events for Eloquent model state changes and marking those events with the ShouldBroadcast interface.

However, if you are not using these events for any other purposes in your application, it can be cumbersome to create event classes for the sole purpose of broadcasting them. To remedy this, Laravel allows you to indicate that an Eloquent model should automatically broadcast its state changes.

To get started, your Eloquent model should use the Illuminate\Database\Eloquent\BroadcastsEvents trait. In addition, the model should define a broadcastOn method, which will return an array of channels that the model's events should broadcast on:

<?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];
    }
}

Once your model includes this trait and defines its broadcast channels, it will begin automatically broadcasting events when a model instance is created, updated, deleted, trashed, or restored.

In addition, you may have noticed that the broadcastOn method receives a string $event argument. This argument contains the type of event that has occurred on the model and will have a value of created, updated, deleted, trashed, or restored. By inspecting the value of this variable, you may determine which channels (if any) the model should broadcast to for a particular event:

/**
 * 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],
    };
}

Customizing Model Broadcasting Event Creation

Occasionally, you may wish to customize how Laravel creates the underlying model broadcasting event. You may accomplish this by defining a newBroadcastableEvent method on your Eloquent model. This method should return an Illuminate\Database\Eloquent\BroadcastableModelEventOccurred instance:

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

Model Broadcasting Conventions

Channel Conventions

As you may have noticed, the broadcastOn method in the model example above did not return Channel instances. Instead, Eloquent models were returned directly. If an Eloquent model instance is returned by your model's broadcastOn method (or is contained in an array returned by the method), Laravel will automatically instantiate a private channel instance for the model using the model's class name and primary key identifier as the channel name.

So, an App\Models\User model with an id of 1 would be converted into an Illuminate\Broadcasting\PrivateChannel instance with a name of App.Models.User.1. Of course, in addition to returning Eloquent model instances from your model's broadcastOn method, you may return complete Channel instances in order to have full control over the model's channel names:

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

If you plan to explicitly return a channel instance from your model's broadcastOn method, you may pass an Eloquent model instance to the channel's constructor. When doing so, Laravel will use the model channel conventions discussed above to convert the Eloquent model into a channel name string:

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

If you need to determine the channel name of a model, you may call the broadcastChannel method on any model instance. For example, this method returns the string App.Models.User.1 for an App\Models\User model with an id of 1:

$user->broadcastChannel();

Event Conventions

Since model broadcast events are not associated with an "actual" event within your application's App\Events directory, they are assigned a name and a payload based on conventions. Laravel's convention is to broadcast the event using the class name of the model (not including the namespace) and the name of the model event that triggered the broadcast.

So, for example, an update to the App\Models\Post model would broadcast an event to your client-side application as PostUpdated with the following payload:

{
    "model": {
        "id": 1,
        "title": "My first post"
        ...
    },
    ...
    "socket": "someSocketId"
}

The deletion of the App\Models\User model would broadcast an event named UserDeleted.

If you would like, you may define a custom broadcast name and payload by adding a broadcastAs and broadcastWith method to your model. These methods receive the name of the model event / operation that is occurring, allowing you to customize the event's name and payload for each model operation. If null is returned from the broadcastAs method, Laravel will use the model broadcasting event name conventions discussed above when broadcasting the event:

/**
 * 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],
    };
}

Listening for Model Broadcasts

Once you have added the BroadcastsEvents trait to your model and defined your model's broadcastOn method, you are ready to start listening for broadcasted model events within your client-side application. Before getting started, you may wish to consult the complete documentation on listening for events.

First, use the private method to retrieve an instance of a channel, then call the listen method to listen for a specified event. Typically, the channel name given to the private method should correspond to Laravel's model broadcasting conventions.

Once you have obtained a channel instance, you may use the listen method to listen for a particular event. Since model broadcast events are not associated with an "actual" event within your application's App\Events directory, the event name must be prefixed with a . to indicate it does not belong to a particular namespace. Each model broadcast event has a model property which contains all of the broadcastable properties of the model:

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

Using React, Vue, or Svelte

If you are using React, Vue, or Svelte, you may use Laravel Echo's included useEchoModel hook to easily listen for model broadcasts:

import { useEchoModel } from "@laravel/echo-react";

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

useEchoModel("App.Models.User", userId, ["UserUpdated"], (e) => {
    console.log(e.model);
});
</script>
<script>
import { useEchoModel } from "@laravel/echo-svelte";

useEchoModel("App.Models.User", userId, ["UserUpdated"], (e) => {
    console.log(e.model);
});
</script>

You may also specify the shape of the model event payload data, providing greater type safety and editing convenience:

type User = {
    id: number;
    name: string;
    email: string;
};

useEchoModel<User, "App.Models.User">("App.Models.User", userId, ["UserUpdated"], (e) => {
    console.log(e.model.id);
    console.log(e.model.name);
});

Client Events

[!NOTE]
When using Pusher Channels, you must enable the "Client Events" option in the "App Settings" section of your application dashboard in order to send client events.

Sometimes you may wish to broadcast an event to other connected clients without hitting your Laravel application at all. This can be particularly useful for things like "typing" notifications, where you want to alert users of your application that another user is typing a message on a given screen.

To broadcast client events, you may use Echo's whisper method:

Echo.private(`chat.${roomId}`)
    .whisper('typing', {
        name: this.user.name
    });
import { useEcho } from "@laravel/echo-react";

const { channel } = useEcho(`chat.${roomId}`, ['update'], (e) => {
    console.log('Chat event received:', e);
});

channel().whisper('typing', { name: user.name });
<script setup lang="ts">
import { useEcho } from "@laravel/echo-vue";

const { channel } = useEcho(`chat.${roomId}`, ['update'], (e) => {
    console.log('Chat event received:', e);
});

channel().whisper('typing', { name: user.name });
</script>
<script>
import { useEcho } from "@laravel/echo-svelte";

const { channel } = useEcho(`chat.${roomId}`, ['update'], (e) => {
    console.log('Chat event received:', e);
});

channel().whisper('typing', { name: user.name });
</script>

To listen for client events, you may use the listenForWhisper method:

Echo.private(`chat.${roomId}`)
    .listenForWhisper('typing', (e) => {
        console.log(e.name);
    });
import { useEcho } from "@laravel/echo-react";

const { channel } = useEcho(`chat.${roomId}`, ['update'], (e) => {
    console.log('Chat event received:', e);
});

channel().listenForWhisper('typing', (e) => {
    console.log(e.name);
});
<script setup lang="ts">
import { useEcho } from "@laravel/echo-vue";

const { channel } = useEcho(`chat.${roomId}`, ['update'], (e) => {
    console.log('Chat event received:', e);
});

channel().listenForWhisper('typing', (e) => {
    console.log(e.name);
});
</script>
<script>
import { useEcho } from "@laravel/echo-svelte";

const { channel } = useEcho(`chat.${roomId}`, ['update'], (e) => {
    console.log('Chat event received:', e);
});

channel().listenForWhisper('typing', (e) => {
    console.log(e.name);
});
</script>

Notifications

By pairing event broadcasting with notifications, your JavaScript application may receive new notifications as they occur without needing to refresh the page. Before getting started, be sure to read over the documentation on using the broadcast notification channel.

Once you have configured a notification to use the broadcast channel, you may listen for the broadcast events using Echo's notification method. Remember, the channel name should match the class name of the entity receiving the notifications:

Echo.private(`App.Models.User.${userId}`)
    .notification((notification) => {
        console.log(notification.type);
    });
import { useEchoModel } from "@laravel/echo-react";

const { channel } = useEchoModel('App.Models.User', userId);

channel().notification((notification) => {
    console.log(notification.type);
});
<script setup lang="ts">
import { useEchoModel } from "@laravel/echo-vue";

const { channel } = useEchoModel('App.Models.User', userId);

channel().notification((notification) => {
    console.log(notification.type);
});
</script>
<script>
import { useEchoModel } from "@laravel/echo-svelte";

const { channel } = useEchoModel('App.Models.User', userId);

channel().notification((notification) => {
    console.log(notification.type);
});
</script>

In this example, all notifications sent to App\Models\User instances via the broadcast channel would be received by the callback. A channel authorization callback for the App.Models.User.{id} channel is included in your application's routes/channels.php file.

Stop Listening for Notifications

If you would like to stop listening to notifications without leaving the channel, you may use the stopListeningForNotification method:

const callback = (notification) => {
    console.log(notification.type);
}

// Start listening...
Echo.private(`App.Models.User.${userId}`)
    .notification(callback);

// Stop listening (callback must be the same)...
Echo.private(`App.Models.User.${userId}`)
    .stopListeningForNotification(callback);

本文章首发在 LearnKu.com 网站上。

本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。

《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
贡献者:1
讨论数量: 3
发起讨论 只看当前版本


全村的希望
Laravel-echo/server 结合 JWT 配置方式
9 个点赞 | 2 个回复 | 分享 | 课程版本 5.6
levi
我想做一个全站通知,请问使用laravel的广播可以实现吗?
0 个点赞 | 3 个回复 | 问答 | 课程版本 9.x
acai2046
Illuminate \ Broadcasting \ BroadcastException No message
0 个点赞 | 0 个回复 | 问答 | 课程版本 5.8