LaravelDocs(中文)

廣播 (Broadcasting)

Laravel 讓你能輕鬆地透過 WebSockets 廣播伺服器端事件

簡介 (Introduction)

在許多現代 Web 應用程式中,WebSockets 用於實現即時、即時更新的使用者介面。當伺服器上的某些資料更新時,通常會透過 WebSocket 連線傳送訊息以供客戶端處理。WebSockets 提供了一種更有效的替代方案,可以持續輪詢應用程式伺服器以獲取應反映在 UI 中的資料變更。

例如,想像你的應用程式能夠將使用者的資料匯出為 CSV 檔案並透過電子郵件傳送給他們。但是,建立此 CSV 檔案需要幾分鐘時間,因此你選擇在 佇列任務 中建立並郵寄 CSV。當 CSV 建立並郵寄給使用者後,我們可以使用事件廣播分派 App\Events\UserDataExported 事件,該事件由我們的應用程式的 JavaScript 接收。收到事件後,我們可以向使用者顯示一條訊息,指出他們的 CSV 已透過電子郵件傳送給他們,而無需重新整理頁面。

為了協助你建立這些類型的功能,Laravel 讓你可以輕鬆地透過 WebSocket 連線「廣播」你的伺服器端 Laravel 事件。廣播你的 Laravel 事件允許你在伺服器端 Laravel 應用程式和客戶端 JavaScript 應用程式之間共用相同的事件名稱和資料。

廣播背後的核心概念很簡單:客戶端連線到前端的命名頻道,而你的 Laravel 應用程式在後端向這些頻道廣播事件。這些事件可以包含你希望提供給前端的任何其他資料。

支援的驅動程式 (Supported Drivers)

預設情況下,Laravel 包含三個伺服器端廣播驅動程式供你選擇:Laravel ReverbPusher ChannelsAbly

[!NOTE] 在深入研究事件廣播之前,請確保你已閱讀 Laravel 關於 事件和監聽器 的文件。

快速入門 (Quickstart)

預設情況下,新的 Laravel 應用程式未啟用廣播。你可以使用 install:broadcasting Artisan 指令啟用廣播:

php artisan install:broadcasting

install:broadcasting 指令將提示你選擇要使用的事件廣播服務。此外,它將建立 config/broadcasting.php 設定檔和 routes/channels.php 檔案,你可以在其中註冊應用程式的廣播授權路由和回呼。

Laravel 開箱即支援多種廣播驅動程式:Laravel ReverbPusher ChannelsAbly 以及用於本機開發和除錯的 log 驅動程式。此外,還包含一個 null 驅動程式,允許你在測試期間停用廣播。config/broadcasting.php 設定檔中包含每個驅動程式的設定範例。

應用程式的所有事件廣播設定都儲存在 config/broadcasting.php 設定檔中。如果你的應用程式中不存在此檔案,請不要擔心;當你執行 install:broadcasting Artisan 指令時,它將被建立。

下一步 (Quickstart Next Steps)

啟用事件廣播後,你就可以準備好了解有關 定義廣播事件監聽事件 的更多資訊。如果你使用的是 Laravel 的 React 或 Vue 入門套件,則可以使用 Echo 的 useEcho hook 監聽事件。

[!NOTE] 在廣播任何事件之前,你應該先設定並執行 佇列工作者。所有事件廣播都是透過佇列任務完成的,以便應用程式的回應時間不會受到廣播事件的嚴重影響。

伺服器端安裝 (Server Side Installation)

要開始使用 Laravel 的事件廣播,我們需要在 Laravel 應用程式中進行一些設定以及安裝一些套件。

事件廣播是透過伺服器端廣播驅動程式完成的,該驅動程式廣播你的 Laravel 事件,以便 Laravel Echo(一個 JavaScript 函式庫)可以在瀏覽器客戶端接收它們。別擔心 - 我們將逐步引導你完成安裝過程的每個部分。

Reverb

要在使用 Reverb 作為事件廣播器時快速啟用對 Laravel 廣播功能的支援,請使用 --reverb 選項呼叫 install:broadcasting Artisan 指令。此 Artisan 指令將安裝 Reverb 所需的 Composer 和 NPM 套件,並使用適當的變數更新應用程式的 .env 檔案:

php artisan install:broadcasting --reverb

手動安裝 (Reverb Manual Installation)

執行 install:broadcasting 指令時,系統會提示你安裝 Laravel Reverb。當然,你也可以使用 Composer 套件管理器手動安裝 Reverb:

composer require laravel/reverb

安裝套件後,你可以執行 Reverb 的安裝指令來發佈設定、新增 Reverb 所需的環境變數,並在應用程式中啟用事件廣播:

php artisan reverb:install

你可以在 Reverb 文件 中找到詳細的 Reverb 安裝和使用說明。

Pusher Channels

要在使用 Pusher 作為事件廣播器時快速啟用對 Laravel 廣播功能的支援,請使用 --pusher 選項呼叫 install:broadcasting Artisan 指令。此 Artisan 指令將提示你輸入 Pusher 憑證,安裝 Pusher PHP 和 JavaScript SDK,並使用適當的變數更新應用程式的 .env 檔案:

php artisan install:broadcasting --pusher

手動安裝 (Pusher Manual Installation)

要手動安裝 Pusher 支援,你應該使用 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 設定還允許你指定 Channels 支援的其他 options,例如叢集。

然後,在應用程式的 .env 檔案中將 BROADCAST_CONNECTION 環境變數設定為 pusher

BROADCAST_CONNECTION=pusher

最後,你就可以準備安裝和設定 Laravel Echo,它將在客戶端接收廣播事件。

Ably

[!NOTE] 下面的文件討論如何在「Pusher 相容性」模式下使用 Ably。但是,Ably 團隊推薦並維護一個廣播器和 Echo 客戶端,能夠利用 Ably 提供的獨特功能。有關使用 Ably 維護的驅動程式的更多資訊,請 查閱 Ably 的 Laravel 廣播器文件

要在使用 Ably 作為事件廣播器時快速啟用對 Laravel 廣播功能的支援,請使用 --ably 選項呼叫 install:broadcasting Artisan 指令。此 Artisan 指令將提示你輸入 Ably 憑證,安裝 Ably PHP 和 JavaScript SDK,並使用適當的變數更新應用程式的 .env 檔案:

php artisan install:broadcasting --ably

在繼續之前,你應該在 Ably 應用程式設定中啟用 Pusher 協定支援。你可以在 Ably 應用程式設定儀表板的「Protocol Adapter Settings」部分啟用此功能。

手動安裝 (Ably Manual Installation)

要手動安裝 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,它將在客戶端接收廣播事件。

客戶端安裝 (Client Side Installation)

Reverb

Laravel Echo 是一個 JavaScript 函式庫,讓你可以輕鬆訂閱頻道並監聽伺服器端廣播驅動程式廣播的事件。

當透過 install:broadcasting Artisan 指令安裝 Laravel Reverb 時,Reverb 和 Echo 的鷹架和設定將自動注入到你的應用程式中。但是,如果你希望手動設定 Laravel Echo,可以按照以下說明進行操作。

手動安裝 (Reverb Client Manual Installation)

要為應用程式的前端手動設定 Laravel Echo,請先安裝 pusher-js 套件,因為 Reverb 使用 Pusher 協定進行 WebSocket 訂閱、頻道和訊息:

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

安裝 Echo 後,你就可以準備在應用程式的 JavaScript 中建立一個新的 Echo 實例。執行此操作的一個好地方是 Laravel 框架包含的 resources/js/bootstrap.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'],
});

接下來,你應該編譯應用程式的資源:

npm run build

[!WARNING] Laravel Echo reverb 廣播器需要 laravel-echo v1.16.0+。

Pusher Channels

Laravel Echo 是一個 JavaScript 函式庫,讓你可以輕鬆訂閱頻道並監聽伺服器端廣播驅動程式廣播的事件。

當透過 install:broadcasting --pusher Artisan 指令安裝廣播支援時,Pusher 和 Echo 的鷹架和設定將自動注入到你的應用程式中。但是,如果你希望手動設定 Laravel Echo,可以按照以下說明進行操作。

手動安裝 (Pusher Client Manual Installation)

要為應用程式的前端手動設定 Laravel Echo,請先安裝 laravel-echopusher-js 套件,它們使用 Pusher 協定進行 WebSocket 訂閱、頻道和訊息:

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

安裝 Echo 後,你就可以準備在應用程式的 resources/js/bootstrap.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"],
});

接下來,你應該在應用程式的 .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

[!NOTE] 要了解有關編譯應用程式 JavaScript 資源的更多資訊,請查閱 Vite 文件。

使用現有的客戶端實例 (Using An Existing Client Instance)

如果你已經有一個預先設定好的 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

[!NOTE] 下面的文件討論如何在「Pusher 相容性」模式下使用 Ably。但是,Ably 團隊推薦並維護一個廣播器和 Echo 客戶端,能夠利用 Ably 提供的獨特功能。有關使用 Ably 維護的驅動程式的更多資訊,請 查閱 Ably 的 Laravel 廣播器文件

Laravel Echo 是一個 JavaScript 函式庫,讓你可以輕鬆訂閱頻道並監聽伺服器端廣播驅動程式廣播的事件。

當透過 install:broadcasting --ably Artisan 指令安裝廣播支援時,Ably 和 Echo 的鷹架和設定將自動注入到你的應用程式中。但是,如果你希望手動設定 Laravel Echo,可以按照以下說明進行操作。

手動安裝 (Ably Client Manual Installation)

要為應用程式的前端手動設定 Laravel Echo,請先安裝 laravel-echopusher-js 套件,它們使用 Pusher 協定進行 WebSocket 訂閱、頻道和訊息:

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

在繼續之前,你應該在 Ably 應用程式設定中啟用 Pusher 協定支援。你可以在 Ably 應用程式設定儀表板的「Protocol Adapter Settings」部分啟用此功能。

安裝 Echo 後,你就可以準備在應用程式的 resources/js/bootstrap.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,
});

你可能已經注意到我們的 Ably Echo 設定引用了 VITE_ABLY_PUBLIC_KEY 環境變數。此變數的值應該是你的 Ably 公開金鑰。你的公開金鑰是 Ably 金鑰中 : 字元之前的部分。

根據你的需求調整 Echo 設定後,你可以編譯應用程式的資源:

npm run dev

[!NOTE] 要了解有關編譯應用程式 JavaScript 資源的更多資訊,請查閱 Vite 文件。

概念總覽 (Concept Overview)

Laravel 的事件廣播允許你使用基於驅動程式的 WebSockets 方法將伺服器端 Laravel 事件廣播到客戶端 JavaScript 應用程式。目前,Laravel 隨附 Laravel ReverbPusher ChannelsAbly 驅動程式。可以使用 Laravel Echo JavaScript 套件在客戶端輕鬆使用這些事件。

事件透過「頻道」廣播,頻道可以指定為公開或私有。應用程式的任何訪客都可以在沒有任何身份驗證或授權的情況下訂閱公開頻道;但是,為了訂閱私有頻道,使用者必須經過身份驗證並獲得授權才能在該頻道上監聽。

使用範例應用程式 (Using an Example Application)

在深入研究事件廣播的每個元件之前,讓我們以電子商務商店為例進行高層次的概述。

在我們的應用程式中,假設我們有一個頁面允許使用者查看其訂單的運送狀態。我們還假設當應用程式處理運送狀態更新時會觸發 OrderShipmentStatusUpdated 事件:

use App\Events\OrderShipmentStatusUpdated;

OrderShipmentStatusUpdated::dispatch($order);

ShouldBroadcast 介面 (The Shouldbroadcast Interface)

當使用者正在查看他們的訂單之一時,我們不希望他們必須重新整理頁面才能查看狀態更新。相反,我們希望在建立更新時將其廣播到應用程式。因此,我們需要使用 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 方法。此方法負責回傳事件應廣播到的頻道。生成的事件類別上已經定義了此方法的空 Stub,因此我們只需要填寫其詳細資訊。我們只希望訂單的建立者能夠查看狀態更新,因此我們將在與訂單繫結的私有頻道上廣播該事件:

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

授權頻道 (Example Application Authorizing Channels)

請記住,使用者必須獲得授權才能在私有頻道上監聽。我們可以在應用程式的 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 方法接受兩個參數:頻道名稱和一個回呼,該回呼回傳 truefalse 以指示使用者是否被授權在頻道上監聽。

所有授權回呼都接收目前經過身份驗證的使用者作為其第一個參數,並接收任何其他萬用字元參數作為其後續參數。在此範例中,我們使用 {orderId} 預留位置來指示頻道名稱的「ID」部分是萬用字元。

監聽事件廣播 (Listening For Event Broadcasts)

接下來,剩下的就是在我們的 JavaScript 應用程式中監聽事件。我們可以使用 Laravel Echo 來完成此操作。Laravel Echo 內建的 React 和 Vue 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>

定義廣播事件 (Defining Broadcast Events)

要通知 Laravel 應該廣播給定的事件,你必須在事件類別上實作 Illuminate\Contracts\Broadcasting\ShouldBroadcast 介面。此介面已匯入到框架產生的所有事件類別中,因此你可以輕鬆地將其新增至任何事件中。

ShouldBroadcast 介面要求你實作一個方法:broadcastOnbroadcastOn 方法應回傳事件應廣播到的一個頻道或頻道陣列。頻道應為 ChannelPrivateChannelPresenceChannel 的實例。Channel 的實例代表任何使用者都可以訂閱的公開頻道,而 PrivateChannelsPresenceChannels 代表需要 頻道授權 的私有頻道:

<?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 介面後,你只需要像往常一樣 觸發事件。觸發事件後,佇列任務 將使用你指定的廣播驅動程式自動廣播該事件。

廣播名稱 (Broadcast Name)

預設情況下,Laravel 將使用事件的類別名稱廣播事件。但是,你可以透過在事件上定義 broadcastAs 方法來自訂廣播名稱:

/**
 * The event's broadcast name.
 */
public function broadcastAs(): string
{
    return 'server.created';
}

如果你使用 broadcastAs 方法自訂廣播名稱,你應該確保使用前導 . 字元註冊你的監聽器。這將指示 Echo 不要將應用程式的命名空間附加到事件:

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

廣播資料 (Broadcast Data)

當事件被廣播時,其所有 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];
}

廣播佇列 (Broadcast Queue)

預設情況下,每個廣播事件都放置在 queue.php 設定檔中指定的預設佇列連線的預設佇列中。你可以透過在事件類別上定義 connectionqueue 屬性來自訂廣播器使用的佇列連線和名稱:

/**
 * 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

namespace App\Events;

use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;

class OrderShipmentStatusUpdated implements ShouldBroadcastNow
{
    // ...
}

廣播條件 (Broadcast Conditions)

有時你只想在給定條件為真時廣播事件。你可以透過在事件類別中新增 broadcastWhen 方法來定義這些條件:

/**
 * Determine if this event should broadcast.
 */
public function broadcastWhen(): bool
{
    return $this->order->value > 100;
}

廣播和資料庫交易 (Broadcasting and Database Transactions)

當廣播事件在資料庫交易中分派時,它們可能會在資料庫交易提交之前由佇列處理。發生這種情況時,你在資料庫交易期間對模型或資料庫記錄所做的任何更新可能尚未反映在資料庫中。此外,在交易中建立的任何模型或資料庫記錄可能不存在於資料庫中。如果你的事件依賴這些模型,則在處理廣播事件的任務時可能會發生意外錯誤。

如果你的佇列連線的 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;
}

[!NOTE] 要了解有關解決這些問題的更多資訊,請查看有關 佇列任務和資料庫交易 的文件。

授權頻道 (Authorizing Channels)

私有頻道要求你授權目前經過身份驗證的使用者實際上可以在頻道上監聽。這是透過向你的 Laravel 應用程式發出帶有頻道名稱的 HTTP 請求並允許你的應用程式確定使用者是否可以在該頻道上監聽來完成的。使用 Laravel Echo 時,將自動發出授權訂閱私有頻道的 HTTP 請求。

安裝廣播時,Laravel 會嘗試自動註冊 /broadcasting/auth 路由來處理授權請求。如果 Laravel 無法自動註冊這些路由,你可以在應用程式的 /bootstrap/app.php 檔案中手動註冊它們:

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

定義授權回呼 (Defining Authorization Callbacks)

接下來,我們需要定義實際確定目前經過身份驗證的使用者是否可以監聽給定頻道的邏輯。這是在 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 方法接受兩個參數:頻道名稱和一個回呼,該回呼回傳 truefalse 以指示使用者是否被授權在頻道上監聽。

所有授權回呼都接收目前經過身份驗證的使用者作為其第一個參數,並接收任何其他萬用字元參數作為其後續參數。在此範例中,我們使用 {orderId} 預留位置來指示頻道名稱的「ID」部分是萬用字元。

你可以使用 channel:list Artisan 指令查看應用程式的廣播授權回呼清單:

php artisan channel:list

授權回呼模型繫結 (Authorization Callback Model Binding)

就像 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;
});

[!WARNING] 與 HTTP 路由模型繫結不同,頻道模型繫結不支援自動 隱式模型繫結範圍。但是,這很少成為問題,因為大多數頻道都可以基於單個模型的唯一主鍵進行範圍設定。

授權回呼身份驗證 (Authorization Callback Authentication)

私有和存在廣播頻道透過應用程式的預設身份驗證守衛對目前使用者進行身份驗證。如果使用者未經過身份驗證,頻道授權將自動被拒絕,並且永遠不會執行授權回呼。但是,你可以分配多個自訂守衛,以便在必要時對傳入請求進行身份驗證:

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

定義頻道類別 (Defining Channel Classes)

如果你的應用程式正在使用許多不同的頻道,你的 routes/channels.php 檔案可能會變得很龐大。因此,你可以使用頻道類別,而不是使用 Closure 來授權頻道。要產生頻道類別,請使用 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 方法將包含你通常放置在頻道授權 Closure 中的相同邏輯。你也可以利用頻道模型繫結:

<?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] 像 Laravel 中的許多其他類別一樣,頻道類別將由 服務容器 自動解析。因此,你可以在其建構函式中對頻道所需的任何依賴項進行型別提示。

廣播事件 (Broadcasting Events)

一旦你定義了一個事件並用 ShouldBroadcast 介面標記了它,你只需要使用事件的分派方法觸發該事件。事件分派器會注意到該事件標記有 ShouldBroadcast 介面,並將該事件排入佇列以進行廣播:

use App\Events\OrderShipmentStatusUpdated;

OrderShipmentStatusUpdated::dispatch($order);

僅傳送給其他人 (Only to Others)

在建置利用事件廣播的應用程式時,你偶爾可能需要將事件廣播給給定頻道的所有訂閱者,除了目前使用者之外。你可以使用 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 trait 才能呼叫 toOthers 方法。

設定 (Only To Others Configuration)

當你初始化 Laravel Echo 實例時,會為連線分配一個 socket ID。如果你使用全域 Axios 實例從 JavaScript 應用程式發出 HTTP 請求,則 socket ID 將自動作為 X-Socket-ID 標頭附加到每個傳出請求。然後,當你呼叫 toOthers 方法時,Laravel 將從標頭中提取 socket ID,並指示廣播器不要廣播到具有該 socket ID 的任何連線。

如果你不使用全域 Axios 實例,則需要手動設定 JavaScript 應用程式以隨所有傳出請求傳送 X-Socket-ID 標頭。你可以使用 Echo.socketId 方法檢索 socket ID:

var socketId = Echo.socketId();

自訂連線 (Customizing the Connection)

如果你的應用程式與多個廣播連線互動,並且你想使用預設廣播器以外的廣播器廣播事件,你可以使用 via 方法指定將事件推送到哪個連線:

use App\Events\OrderShipmentStatusUpdated;

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

或者,你可以透過在事件的建構函式中呼叫 broadcastVia 方法來指定事件的廣播連線。但是,在這樣做之前,你應該確保事件類別使用 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)

有時,你可能希望將簡單事件廣播到應用程式的前端,而無需建立專用的事件類別。為了適應這種情況,Broadcast facade 允許你廣播「匿名事件」:

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

上面的範例將廣播如下事件:

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

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

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

挽救廣播 (Rescuing Broadcasts)

當你的應用程式的佇列伺服器不可用或 Laravel 在廣播事件時遇到錯誤時,會拋出例外,通常會導致最終使用者看到應用程式錯誤。由於事件廣播通常是應用程式核心功能的補充,因此你可以透過在事件上實作 ShouldRescue 介面來防止這些例外中斷使用者體驗。

實作 ShouldRescue 介面的事件在廣播嘗試期間會自動利用 Laravel 的 rescue 輔助函式。此輔助函式會擷取任何例外,將其報告給應用程式的例外處理常式進行記錄,並允許應用程式繼續正常執行而不會中斷使用者的工作流程:

<?php

namespace App\Events;

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

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

接收廣播 (Receiving Broadcasts)

監聽事件 (Listening for Events)

一旦你 安裝並實例化了 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(/* ... */);

停止監聽事件 (Stop Listening For Events)

如果你想停止監聽給定的事件而不 離開頻道,可以使用 stopListening 方法:

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

離開頻道 (Leaving a Channel)

要離開頻道,你可以在 Echo 實例上呼叫 leaveChannel 方法:

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

如果你想離開頻道及其關聯的私有和存在頻道,可以呼叫 leave 方法:

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

命名空間 (Namespaces)

你可能已經注意到在上面的範例中,我們沒有為事件類別指定完整的 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) => {
  // ...
});

使用 React 或 Vue (Using React or Vue)

Laravel Echo 包含 React 和 Vue hooks,讓監聽事件變得輕鬆。要開始使用,請呼叫 useEcho hook,它用於監聽私有事件。當使用元件卸載時,useEcho hook 將自動離開頻道:

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>

你可以透過向 useEcho 提供事件陣列來監聽多個事件:

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

你也可以指定廣播事件負載資料的形狀,提供更好的型別安全性和編輯便利性:

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

useEcho hook 將在消費元件卸載時自動離開頻道;但是,你可以在必要時利用回傳的函式以程式設計方式手動停止/開始監聽頻道:

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>

連接到公開頻道 (React Vue Connecting To Public Channels)

要連接到公開頻道,你可以使用 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>

連接到存在頻道 (React Vue Connecting To Presence Channels)

要連接到存在頻道,你可以使用 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>

存在頻道 (Presence Channels)

存在頻道建立在私有頻道的安全性之上,同時公開了誰訂閱了頻道的額外功能。這使得建置強大的協作應用程式功能變得容易,例如當另一個使用者正在查看同一頁面時通知使用者,或列出聊天室的成員。

授權存在頻道 (Authorizing Presence Channels)

所有存在頻道也是私有頻道;因此,使用者必須 被授權存取它們。但是,在為存在頻道定義授權回呼時,如果使用者被授權加入頻道,你將不會回傳 true。相反,你應該回傳有關使用者的資料陣列。

授權回呼回傳的資料將可供 JavaScript 應用程式中的存在頻道事件監聽器使用。如果使用者未被授權加入存在頻道,你應該回傳 falsenull

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)

要加入存在頻道,你可以使用 Echo 的 join 方法。join 方法將回傳一個 PresenceChannel 實作,該實作除了公開 listen 方法外,還允許你訂閱 herejoiningleaving 事件。

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 出現問題時執行。

廣播到存在頻道 (Broadcasting to Presence Channels)

存在頻道可以像公開或私有頻道一樣接收事件。以聊天室為例,我們可能希望將 NewMessage 事件廣播到聊天室的存在頻道。為此,我們將從事件的 broadcastOn 方法回傳 PresenceChannel 的實例:

/**
 * Get the channels the event should broadcast on.
 *
 * @return array<int, \Illuminate\Broadcasting\Channel>
 */
public function broadcastOn(): array
{
    return [
        new PresenceChannel('chat.'.$this->message->room_id),
    ];
}

與其他事件一樣,你可以使用 broadcast 輔助函式和 toOthers 方法來排除目前使用者接收廣播:

broadcast(new NewMessage($message));

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

與其他類型的事件一樣,你可以使用 Echo 的 listen 方法監聽傳送到存在頻道的事件:

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

模型廣播 (Model Broadcasting)

[!WARNING] 在閱讀以下有關模型廣播的文件之前,我們建議你熟悉 Laravel 模型廣播服務的一般概念,以及如何手動建立和監聽廣播事件。

當應用程式的 Eloquent 模型 被建立、更新或刪除時,廣播事件是很常見的。當然,這可以透過手動 為 Eloquent 模型狀態變更定義自訂事件 並使用 ShouldBroadcast 介面標記這些事件來輕鬆完成。

但是,如果你沒有在應用程式中將這些事件用於任何其他目的,那麼僅為了廣播而建立事件類別可能會很麻煩。為了解決這個問題,Laravel 允許你指示 Eloquent 模型應自動廣播其狀態變更。

要開始使用,你的 Eloquent 模型應使用 Illuminate\Database\Eloquent\BroadcastsEvents trait。此外,模型應定義一個 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];
    }
}

一旦你的模型包含了這個 trait 並定義了它的廣播頻道,當模型實例被建立、更新、刪除、移至垃圾桶或還原時,它將開始自動廣播事件。

此外,你可能已經注意到 broadcastOn 方法接收一個字串 $event 參數。此參數包含模型上發生的事件類型,其值為 createdupdateddeletedtrashedrestored。透過檢查此變數的值,你可以確定模型應針對特定事件廣播到哪些頻道(如果有的話):

/**
 * 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)

偶爾,你可能希望自訂 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();
}

模型廣播慣例 (Model Broadcasting Conventions)

頻道慣例 (Model Broadcasting Channel Conventions)

你可能已經注意到,上述模型範例中的 broadcastOn 方法沒有回傳 Channel 實例。相反,直接回傳了 Eloquent 模型。如果你的模型的 broadcastOn 方法回傳了 Eloquent 模型實例(或包含在該方法回傳的陣列中),Laravel 將使用模型的類別名稱和主鍵識別碼作為頻道名稱,自動為該模型實例化一個私有頻道實例。

因此,一個 id1App\Models\User 模型將被轉換為一個名為 App.Models.User.1Illuminate\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 方法。例如,此方法對於 id1App\Models\User 模型回傳字串 App.Models.User.1

$user->broadcastChannel();

事件慣例 (Model Broadcasting Event Conventions)

由於模型廣播事件與應用程式 App\Events 目錄中的「實際」事件無關,因此它們是根據慣例分配名稱和負載的。Laravel 的慣例是使用模型的類別名稱(不包括命名空間)和觸發廣播的模型事件名稱來廣播事件。

因此,例如,對 App\Models\Post 模型的更新將作為 PostUpdated 廣播到你的客戶端應用程式,並帶有以下負載:

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

App\Models\User 模型的刪除將廣播名為 UserDeleted 的事件。

如果你願意,你可以透過在模型中新增 broadcastAsbroadcastWith 方法來自訂廣播名稱和負載。這些方法接收正在發生的模型事件/操作的名稱,允許你為每個模型操作自訂事件的名稱和負載。如果從 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],
    };
}

監聽模型廣播 (Listening for Model Broadcasts)

一旦你將 BroadcastsEvents trait 新增到你的模型並定義了模型的 broadcastOn 方法,你就可以準備開始在客戶端應用程式中監聽廣播的模型事件。在開始之前,你可能希望查閱有關 監聽事件 的完整文件。

首先,使用 private 方法檢索頻道的實例,然後呼叫 listen 方法來監聽指定的事件。通常,提供給 private 方法的頻道名稱應對應於 Laravel 的 模型廣播慣例

一旦你獲得了頻道實例,就可以使用 listen 方法來監聽特定事件。由於模型廣播事件與應用程式 App\Events 目錄中的「實際」事件無關,因此 事件名稱 必須加上 . 前綴,以指示它不屬於特定命名空間。每個模型廣播事件都有一個 model 屬性,其中包含模型的所有可廣播屬性:

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

使用 React 或 Vue (Model Broadcasts With React Or Vue)

如果你使用 React 或 Vue,你可以使用 Laravel Echo 包含的 useEchoModel hook 輕鬆監聽模型廣播:

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>

你也可以指定模型事件負載資料的形狀,提供更好的型別安全性和編輯便利性:

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] 使用 Pusher Channels 時,你必須在 應用程式儀表板 的「App Settings」部分啟用「Client Events」選項,才能傳送客戶端事件。

有時你可能希望將事件廣播給其他連接的客戶端,而根本不經過你的 Laravel 應用程式。這對於「正在輸入」通知之類的功能特別有用,你想在這些功能中提醒應用程式的使用者另一個使用者正在給定螢幕上輸入訊息。

要廣播客戶端事件,你可以使用 Echo 的 whisper 方法:

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>

要監聽客戶端事件,你可以使用 listenForWhisper 方法:

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>

通知 (Notifications)

透過將事件廣播與 通知 配對,你的 JavaScript 應用程式可以在新通知發生時接收它們,而無需重新整理頁面。在開始之前,請務必閱讀有關使用 廣播通知頻道 的文件。

一旦你將通知設定為使用廣播頻道,你就可以使用 Echo 的 notification 方法來監聽廣播事件。請記住,頻道名稱應與接收通知的實體的類別名稱相符:

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>

在此範例中,透過 broadcast 頻道傳送到 App\Models\User 實例的所有通知都將由回呼接收。App.Models.User.{id} 頻道的頻道授權回呼包含在應用程式的 routes/channels.php 檔案中。

停止監聽通知 (Stop Listening For Notifications)

如果你想停止監聽通知而不 離開頻道,可以使用 stopListeningForNotification 方法:

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