跳到內容

廣播

簡介

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

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

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

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

支援的驅動程式

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

在深入探討事件廣播之前,請確保您已閱讀 Laravel 關於事件與監聽器的文件。

伺服器端安裝

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

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

設定

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

Laravel 預設支援多個廣播驅動程式:Laravel ReverbPusher ChannelsAbly,以及用於本地開發和偵錯的 log 驅動程式。此外,還包含一個 null 驅動程式,可讓您在測試期間停用廣播。config/broadcasting.php 設定檔中包含每個驅動程式的設定範例。

安裝

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

1php artisan install:broadcasting

install:broadcasting 命令將建立 config/broadcasting.php 設定檔。此外,該命令還將建立 routes/channels.php 檔案,您可以在其中註冊應用程式的廣播授權路由和回呼。

佇列設定

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

Reverb

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

1composer require laravel/reverb

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

1php artisan reverb:install

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

Pusher Channels

如果您計劃使用 Pusher Channels 廣播您的事件,您應使用 Composer 套件管理器安裝 Pusher Channels PHP SDK

1composer require pusher/pusher-php-server

接下來,您應在 config/broadcasting.php 設定檔中設定您的 Pusher Channels 憑證。此檔案中已包含 Pusher Channels 設定範例,可讓您快速指定您的金鑰、密鑰和應用程式 ID。通常,您應在應用程式的 .env 檔案中設定您的 Pusher Channels 憑證

1PUSHER_APP_ID="your-pusher-app-id"
2PUSHER_APP_KEY="your-pusher-key"
3PUSHER_APP_SECRET="your-pusher-secret"
4PUSHER_HOST=
5PUSHER_PORT=443
6PUSHER_SCHEME="https"
7PUSHER_APP_CLUSTER="mt1"

config/broadcasting.php 檔案的 pusher 設定也允許您指定 Channels 支援的其他 options,例如叢集。

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

1BROADCAST_CONNECTION=pusher

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

Ably

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

如果您計劃使用 Ably 廣播您的事件,您應使用 Composer 套件管理器安裝 Ably PHP SDK

1composer require ably/ably-php

接下來,您應在 config/broadcasting.php 設定檔中設定您的 Ably 憑證。此檔案中已包含 Ably 設定範例,可讓您快速指定您的金鑰。通常,此值應透過 ABLY_KEY 環境變數設定

1ABLY_KEY=your-ably-key

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

1BROADCAST_CONNECTION=ably

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

客戶端安裝

Reverb

Laravel Echo 是一個 JavaScript 函式庫,可讓您輕鬆訂閱頻道並監聽伺服器端廣播驅動程式廣播的事件。您可以透過 NPM 套件管理器安裝 Echo。在此範例中,我們也將安裝 pusher-js 套件,因為 Reverb 使用 Pusher 協定進行 WebSocket 訂閱、頻道和訊息傳遞

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

安裝 Echo 後,您可以準備在應用程式的 JavaScript 中建立一個新的 Echo 實例。一個很好的位置是在 Laravel 框架隨附的 resources/js/bootstrap.js 檔案底部。預設情況下,此檔案中已包含 Echo 設定範例 – 您只需取消註解並將 broadcaster 設定選項更新為 reverb

1import Echo from 'laravel-echo';
2 
3import Pusher from 'pusher-js';
4window.Pusher = Pusher;
5 
6window.Echo = new Echo({
7 broadcaster: 'reverb',
8 key: import.meta.env.VITE_REVERB_APP_KEY,
9 wsHost: import.meta.env.VITE_REVERB_HOST,
10 wsPort: import.meta.env.VITE_REVERB_PORT,
11 wssPort: import.meta.env.VITE_REVERB_PORT,
12 forceTLS: (import.meta.env.VITE_REVERB_SCHEME ?? 'https') === 'https',
13 enabledTransports: ['ws', 'wss'],
14});

接下來,您應編譯應用程式的資源

1npm run build

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

Pusher Channels

Laravel Echo 是一個 JavaScript 函式庫,可讓您輕鬆訂閱頻道並監聽伺服器端廣播驅動程式廣播的事件。Echo 也利用 pusher-js NPM 套件來實作 Pusher 協定,用於 WebSocket 訂閱、頻道和訊息傳遞。

install:broadcasting Artisan 命令會自動為您安裝 laravel-echopusher-js 套件;但是,您也可以透過 NPM 手動安裝這些套件

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

安裝 Echo 後,您可以準備在應用程式的 JavaScript 中建立一個新的 Echo 實例。install:broadcasting 命令會在 resources/js/echo.js 建立一個 Echo 設定檔;但是,此檔案中的預設設定適用於 Laravel Reverb。您可以複製以下設定以將您的設定轉換為 Pusher

1import Echo from 'laravel-echo';
2 
3import Pusher from 'pusher-js';
4window.Pusher = Pusher;
5 
6window.Echo = new Echo({
7 broadcaster: 'pusher',
8 key: import.meta.env.VITE_PUSHER_APP_KEY,
9 cluster: import.meta.env.VITE_PUSHER_APP_CLUSTER,
10 forceTLS: true
11});

接下來,您應在應用程式的 .env 檔案中定義 Pusher 環境變數的適當值。如果這些變數尚未存在於您的 .env 檔案中,您應新增它們

1PUSHER_APP_ID="your-pusher-app-id"
2PUSHER_APP_KEY="your-pusher-key"
3PUSHER_APP_SECRET="your-pusher-secret"
4PUSHER_HOST=
5PUSHER_PORT=443
6PUSHER_SCHEME="https"
7PUSHER_APP_CLUSTER="mt1"
8 
9VITE_APP_NAME="${APP_NAME}"
10VITE_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
11VITE_PUSHER_HOST="${PUSHER_HOST}"
12VITE_PUSHER_PORT="${PUSHER_PORT}"
13VITE_PUSHER_SCHEME="${PUSHER_SCHEME}"
14VITE_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"

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

1npm run build

若要深入瞭解如何編譯應用程式的 JavaScript 資源,請查閱關於 Vite 的文件。

使用現有的客戶端實例

如果您已經有一個預先設定的 Pusher Channels 客戶端實例,並且您希望 Echo 使用它,您可以透過 client 設定選項將其傳遞給 Echo

1import Echo from 'laravel-echo';
2import Pusher from 'pusher-js';
3 
4const options = {
5 broadcaster: 'pusher',
6 key: 'your-pusher-channels-key'
7}
8 
9window.Echo = new Echo({
10 ...options,
11 client: new Pusher(options.key, options)
12});

Ably

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

Laravel Echo 是一個 JavaScript 函式庫,可讓您輕鬆訂閱頻道並監聽伺服器端廣播驅動程式廣播的事件。Echo 也利用 pusher-js NPM 套件來實作 Pusher 協定,用於 WebSocket 訂閱、頻道和訊息傳遞。

install:broadcasting Artisan 命令會自動為您安裝 laravel-echopusher-js 套件;但是,您也可以透過 NPM 手動安裝這些套件

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

在繼續之前,您應在您的 Ably 應用程式設定中啟用 Pusher 協定支援。您可以在 Ably 應用程式設定儀表板的「協定轉接器設定」部分啟用此功能。

安裝 Echo 後,您可以準備在應用程式的 JavaScript 中建立一個新的 Echo 實例。install:broadcasting 命令會在 resources/js/echo.js 建立一個 Echo 設定檔;但是,此檔案中的預設設定適用於 Laravel Reverb。您可以複製以下設定以將您的設定轉換為 Ably

1import Echo from 'laravel-echo';
2 
3import Pusher from 'pusher-js';
4window.Pusher = Pusher;
5 
6window.Echo = new Echo({
7 broadcaster: 'pusher',
8 key: import.meta.env.VITE_ABLY_PUBLIC_KEY,
9 wsHost: 'realtime-pusher.ably.io',
10 wsPort: 443,
11 disableStats: true,
12 encrypted: true,
13});

您可能已經注意到我們的 Ably Echo 設定參考了 VITE_ABLY_PUBLIC_KEY 環境變數。此變數的值應為您的 Ably 公鑰。您的公鑰是您的 Ably 金鑰中在 : 字元之前的部分。

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

1npm run dev

若要深入瞭解如何編譯應用程式的 JavaScript 資源,請查閱關於 Vite 的文件。

概念概述

Laravel 的事件廣播讓您可以使用基於驅動程式的方法將伺服器端的 Laravel 事件廣播到客戶端的 JavaScript 應用程式。目前,Laravel 隨附 Pusher ChannelsAbly 驅動程式。可以使用 Laravel Echo JavaScript 套件在客戶端輕鬆取用事件。

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

使用範例應用程式

在深入探討事件廣播的每個組件之前,讓我們先使用電子商務商店作為範例進行高階概述。

在我們的應用程式中,假設我們有一個頁面允許使用者檢視其訂單的出貨狀態。我們也假設當應用程式處理出貨狀態更新時,會觸發 OrderShipmentStatusUpdated 事件

1use App\Events\OrderShipmentStatusUpdated;
2 
3OrderShipmentStatusUpdated::dispatch($order);

ShouldBroadcast 介面

當使用者正在檢視他們的其中一個訂單時,我們不希望他們必須重新整理頁面才能檢視狀態更新。相反地,我們希望在建立更新時將更新廣播到應用程式。因此,我們需要使用 ShouldBroadcast 介面標記 OrderShipmentStatusUpdated 事件。這將指示 Laravel 在觸發事件時廣播該事件

1<?php
2 
3namespace App\Events;
4 
5use App\Models\Order;
6use Illuminate\Broadcasting\Channel;
7use Illuminate\Broadcasting\InteractsWithSockets;
8use Illuminate\Broadcasting\PresenceChannel;
9use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
10use Illuminate\Queue\SerializesModels;
11 
12class OrderShipmentStatusUpdated implements ShouldBroadcast
13{
14 /**
15 * The order instance.
16 *
17 * @var \App\Models\Order
18 */
19 public $order;
20}

ShouldBroadcast 介面要求我們的事件定義 broadcastOn 方法。此方法負責傳回事件應廣播到的頻道。產生的事件類別上已定義此方法的空 Stub,因此我們只需要填寫其詳細資訊即可。我們只希望訂單的建立者能夠檢視狀態更新,因此我們將在與訂單相關聯的私密頻道上廣播事件

1use Illuminate\Broadcasting\Channel;
2use Illuminate\Broadcasting\PrivateChannel;
3 
4/**
5 * Get the channel the event should broadcast on.
6 */
7public function broadcastOn(): Channel
8{
9 return new PrivateChannel('orders.'.$this->order->id);
10}

如果您希望事件在多個頻道上廣播,您可以傳回 array 作為替代方案

1use Illuminate\Broadcasting\PrivateChannel;
2 
3/**
4 * Get the channels the event should broadcast on.
5 *
6 * @return array<int, \Illuminate\Broadcasting\Channel>
7 */
8public function broadcastOn(): array
9{
10 return [
11 new PrivateChannel('orders.'.$this->order->id),
12 // ...
13 ];
14}

授權頻道

請記住,使用者必須經過授權才能在私密頻道上監聽。我們可以在應用程式的 routes/channels.php 檔案中定義頻道授權規則。在此範例中,我們需要驗證嘗試在私密 orders.1 頻道上監聽的任何使用者實際上是否為訂單的建立者

1use App\Models\Order;
2use App\Models\User;
3 
4Broadcast::channel('orders.{orderId}', function (User $user, int $orderId) {
5 return $user->id === Order::findOrNew($orderId)->user_id;
6});

channel 方法接受兩個引數:頻道名稱和一個回呼,該回呼傳回 truefalse,指示使用者是否已授權在頻道上監聽。

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

監聽事件廣播

接下來,剩下的就是監聽 JavaScript 應用程式中的事件。我們可以使用 Laravel Echo 來完成此操作。首先,我們將使用 private 方法訂閱私密頻道。然後,我們可以使用 listen 方法監聽 OrderShipmentStatusUpdated 事件。預設情況下,所有事件的公開屬性都將包含在廣播事件中

1Echo.private(`orders.${orderId}`)
2 .listen('OrderShipmentStatusUpdated', (e) => {
3 console.log(e.order);
4 });

定義廣播事件

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

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

1<?php
2 
3namespace App\Events;
4 
5use App\Models\User;
6use Illuminate\Broadcasting\Channel;
7use Illuminate\Broadcasting\InteractsWithSockets;
8use Illuminate\Broadcasting\PresenceChannel;
9use Illuminate\Broadcasting\PrivateChannel;
10use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
11use Illuminate\Queue\SerializesModels;
12 
13class ServerCreated implements ShouldBroadcast
14{
15 use SerializesModels;
16 
17 /**
18 * Create a new event instance.
19 */
20 public function __construct(
21 public User $user,
22 ) {}
23 
24 /**
25 * Get the channels the event should broadcast on.
26 *
27 * @return array<int, \Illuminate\Broadcasting\Channel>
28 */
29 public function broadcastOn(): array
30 {
31 return [
32 new PrivateChannel('user.'.$this->user->id),
33 ];
34 }
35}

實作 ShouldBroadcast 介面後,您只需要像平常一樣觸發事件即可。事件觸發後,佇列任務將自動使用您指定的廣播驅動程式廣播事件。

廣播名稱

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

1/**
2 * The event's broadcast name.
3 */
4public function broadcastAs(): string
5{
6 return 'server.created';
7}

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

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

廣播資料

當事件廣播時,其所有 public 屬性都會自動序列化並作為事件的酬載廣播,讓您可以從 JavaScript 應用程式存取其任何公開資料。因此,舉例來說,如果您的事件具有包含 Eloquent 模型的單一公開 $user 屬性,則事件的廣播酬載將為

1{
2 "user": {
3 "id": 1,
4 "name": "Patrick Stewart"
5 ...
6 }
7}

但是,如果您希望更精細地控制您的廣播酬載,您可以將 broadcastWith 方法新增至您的事件。此方法應傳回您希望廣播為事件酬載的資料陣列

1/**
2 * Get the data to broadcast.
3 *
4 * @return array<string, mixed>
5 */
6public function broadcastWith(): array
7{
8 return ['id' => $this->user->id];
9}

廣播佇列

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

1/**
2 * The name of the queue connection to use when broadcasting the event.
3 *
4 * @var string
5 */
6public $connection = 'redis';
7 
8/**
9 * The name of the queue on which to place the broadcasting job.
10 *
11 * @var string
12 */
13public $queue = 'default';

或者,您可以透過在事件上定義 broadcastQueue 方法來自訂佇列名稱

1/**
2 * The name of the queue on which to place the broadcasting job.
3 */
4public function broadcastQueue(): string
5{
6 return 'default';
7}

如果您想要使用 sync 佇列而不是預設佇列驅動程式來廣播事件,您可以實作 ShouldBroadcastNow 介面,而不是 ShouldBroadcast

1<?php
2 
3use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;
4 
5class OrderShipmentStatusUpdated implements ShouldBroadcastNow
6{
7 // ...
8}

廣播條件

有時,您只想在給定條件為 true 時才廣播您的事件。您可以透過將 broadcastWhen 方法新增至您的事件類別來定義這些條件

1/**
2 * Determine if this event should broadcast.
3 */
4public function broadcastWhen(): bool
5{
6 return $this->order->value > 100;
7}

廣播與資料庫交易

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

如果您的佇列連線的 after_commit 設定選項設定為 false,您仍然可以透過在事件類別上實作 ShouldDispatchAfterCommit 介面來指示應在所有開啟的資料庫交易提交後分發特定廣播事件

1<?php
2 
3namespace App\Events;
4 
5use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
6use Illuminate\Contracts\Events\ShouldDispatchAfterCommit;
7use Illuminate\Queue\SerializesModels;
8 
9class ServerCreated implements ShouldBroadcast, ShouldDispatchAfterCommit
10{
11 use SerializesModels;
12}

若要深入瞭解如何解決這些問題,請檢閱關於佇列任務和資料庫交易的文件。

授權頻道

私密頻道要求您授權目前已驗證的使用者實際上可以在頻道上監聽。這是透過向您的 Laravel 應用程式發出 HTTP 請求,並提供頻道名稱,讓您的應用程式判斷使用者是否可以在該頻道上監聽來完成的。當使用 Laravel Echo 時,將會自動發出 HTTP 請求以授權訂閱私密頻道。

啟用廣播後,Laravel 會自動註冊 /broadcasting/auth 路由來處理授權請求。/broadcasting/auth 路由會自動放置在 web 中介層群組中。

定義授權回呼

接下來,我們需要定義實際判斷目前已驗證的使用者是否可以在給定頻道上監聽的邏輯。這是在 install:broadcasting Artisan 命令建立的 routes/channels.php 檔案中完成的。在此檔案中,您可以使用 Broadcast::channel 方法註冊頻道授權回呼

1use App\Models\User;
2 
3Broadcast::channel('orders.{orderId}', function (User $user, int $orderId) {
4 return $user->id === Order::findOrNew($orderId)->user_id;
5});

channel 方法接受兩個引數:頻道名稱和一個回呼,該回呼傳回 truefalse,指示使用者是否已授權在頻道上監聽。

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

您可以使用 channel:list Artisan 命令檢視應用程式的廣播授權回呼清單

1php artisan channel:list

授權回呼模型綁定

就像 HTTP 路由一樣,頻道路由也可以利用隱含和顯式路由模型綁定。例如,您可以請求實際的 Order 模型實例,而不是接收字串或數值訂單 ID

1use App\Models\Order;
2use App\Models\User;
3 
4Broadcast::channel('orders.{order}', function (User $user, Order $order) {
5 return $user->id === $order->user_id;
6});

與 HTTP 路由模型綁定不同,頻道模型綁定不支援自動隱含模型綁定範圍界定。但是,這很少成為問題,因為大多數頻道都可以根據單一模型的唯一主鍵來界定範圍。

授權回呼身份驗證

私密和 Presence 廣播頻道透過您應用程式的預設身份驗證守衛來驗證目前使用者。如果使用者未通過身份驗證,則會自動拒絕頻道授權,並且永遠不會執行授權回呼。但是,您可以視需要指派多個應驗證傳入請求的自訂守衛

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

定義頻道類別

如果您的應用程式正在取用許多不同的頻道,則您的 routes/channels.php 檔案可能會變得臃腫。因此,您可以改為使用頻道類別,而不是使用閉包來授權頻道。若要產生頻道類別,請使用 make:channel Artisan 命令。此命令會將新的頻道類別放置在 App/Broadcasting 目錄中。

1php artisan make:channel OrderChannel

接下來,在您的 routes/channels.php 檔案中註冊您的頻道

1use App\Broadcasting\OrderChannel;
2 
3Broadcast::channel('orders.{order}', OrderChannel::class);

最後,您可以將頻道的授權邏輯放置在頻道類別的 join 方法中。此 join 方法將包含您通常會放置在頻道授權閉包中的相同邏輯。您也可以利用頻道模型綁定

1<?php
2 
3namespace App\Broadcasting;
4 
5use App\Models\Order;
6use App\Models\User;
7 
8class OrderChannel
9{
10 /**
11 * Create a new channel instance.
12 */
13 public function __construct() {}
14 
15 /**
16 * Authenticate the user's access to the channel.
17 */
18 public function join(User $user, Order $order): array|bool
19 {
20 return $user->id === $order->user_id;
21 }
22}

與 Laravel 中的許多其他類別一樣,頻道類別將由服務容器自動解析。因此,您可以在其建構函式中類型提示您的頻道所需的任何相依性。

廣播事件

一旦您定義了一個事件並使用 ShouldBroadcast 介面標記它,您只需要使用事件的 dispatch 方法觸發事件即可。事件分發器將注意到事件已使用 ShouldBroadcast 介面標記,並將事件排入佇列以進行廣播

1use App\Events\OrderShipmentStatusUpdated;
2 
3OrderShipmentStatusUpdated::dispatch($order);

僅對他人廣播

在建構使用事件廣播的應用程式時,您有時可能需要將事件廣播給給定頻道的所有訂閱者,但目前使用者除外。您可以使用 broadcast 輔助函式和 toOthers 方法來完成此操作

1use App\Events\OrderShipmentStatusUpdated;
2 
3broadcast(new OrderShipmentStatusUpdated($update))->toOthers();

為了更好地理解何時可能想要使用 toOthers 方法,讓我們想像一個任務清單應用程式,使用者可以透過輸入任務名稱來建立新任務。若要建立任務,您的應用程式可能會向 /task URL 發出請求,該請求會廣播任務的建立並傳回新任務的 JSON 表示法。當您的 JavaScript 應用程式收到來自端點的回應時,它可能會像這樣直接將新任務插入其任務清單中

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

但是,請記住,我們也會廣播任務的建立。如果您的 JavaScript 應用程式也在監聽此事件以將任務新增至任務清單,則您的清單中將有重複的任務:一個來自端點,另一個來自廣播。您可以透過使用 toOthers 方法來指示廣播器不要向目前使用者廣播事件來解決此問題。

您的事件必須使用 Illuminate\Broadcasting\InteractsWithSockets Trait 才能呼叫 toOthers 方法。

設定

當您初始化 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

1var socketId = Echo.socketId();

自訂連線

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

1use App\Events\OrderShipmentStatusUpdated;
2 
3broadcast(new OrderShipmentStatusUpdated($update))->via('pusher');

或者,您可以在事件的建構函式中呼叫 broadcastVia 方法來指定事件的廣播連線。但是,在執行此操作之前,您應確保事件類別使用 InteractsWithBroadcasting Trait

1<?php
2 
3namespace App\Events;
4 
5use Illuminate\Broadcasting\Channel;
6use Illuminate\Broadcasting\InteractsWithBroadcasting;
7use Illuminate\Broadcasting\InteractsWithSockets;
8use Illuminate\Broadcasting\PresenceChannel;
9use Illuminate\Broadcasting\PrivateChannel;
10use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
11use Illuminate\Queue\SerializesModels;
12 
13class OrderShipmentStatusUpdated implements ShouldBroadcast
14{
15 use InteractsWithBroadcasting;
16 
17 /**
18 * Create a new event instance.
19 */
20 public function __construct()
21 {
22 $this->broadcastVia('pusher');
23 }
24}

匿名事件

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

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

上面的範例將廣播以下事件

1{
2 "event": "AnonymousEvent",
3 "data": "[]",
4 "channel": "orders.1"
5}

使用 aswith 方法,您可以自訂事件的名稱和資料

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

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

1{
2 "event": "OrderPlaced",
3 "data": "{ id: 1, total: 100 }",
4 "channel": "orders.1"
5}

如果您想要在私密或 Presence 頻道上廣播匿名事件,您可以使用 privatepresence 方法

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

使用 send 方法廣播匿名事件會將事件分發到應用程式的佇列以進行處理。但是,如果您想要立即廣播事件,您可以使用 sendNow 方法

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

若要將事件廣播給除目前已驗證使用者以外的所有頻道訂閱者,您可以調用 toOthers 方法

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

接收廣播

監聽事件

一旦您安裝並實例化 Laravel Echo,您就可以開始監聽從您的 Laravel 應用程式廣播的事件。首先,使用 channel 方法來取得頻道的實例,然後呼叫 listen 方法來監聽指定的事件。

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

如果您想要監聽私有頻道上的事件,請改用 private 方法。您可以繼續鏈式呼叫 listen 方法,以便在單一頻道上監聽多個事件。

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

停止監聽事件

如果您想要停止監聽特定的事件,但不想離開頻道,您可以使用 stopListening 方法。

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

離開頻道

若要離開頻道,您可以對您的 Echo 實例呼叫 leaveChannel 方法。

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

如果您想要離開頻道,並且也離開其相關聯的私有頻道和 presence 頻道,您可以呼叫 leave 方法。

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

命名空間

您可能已經注意到在上面的範例中,我們沒有為事件類別指定完整的 App\Events 命名空間。這是因為 Echo 會自動假設事件位於 App\Events 命名空間中。但是,您可以在實例化 Echo 時,透過傳遞 namespace 設定選項來設定根命名空間。

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

或者,您可以在使用 Echo 訂閱事件類別時,在事件類別前加上 . 作為前綴。這樣您就可以始終指定完整的類別名稱。

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

Presence Channels

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

授權 Presence Channels

所有 presence 頻道也是私有頻道;因此,使用者必須經過授權才能存取它們。但是,當為 presence 頻道定義授權回呼時,如果使用者被授權加入頻道,您不會回傳 true。相反地,您應該回傳一個關於使用者的資料陣列。

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

1use App\Models\User;
2 
3Broadcast::channel('chat.{roomId}', function (User $user, int $roomId) {
4 if ($user->canJoinRoom($roomId)) {
5 return ['id' => $user->id, 'name' => $user->name];
6 }
7});

加入 Presence Channels

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

1Echo.join(`chat.${roomId}`)
2 .here((users) => {
3 // ...
4 })
5 .joining((user) => {
6 console.log(user.name);
7 })
8 .leaving((user) => {
9 console.log(user.name);
10 })
11 .error((error) => {
12 console.error(error);
13 });

here 回呼將在頻道成功加入後立即執行,並將接收一個包含目前已訂閱頻道的所有其他使用者資訊的陣列。joining 方法將在新使用者加入頻道時執行,而 leaving 方法將在使用者離開頻道時執行。當身份驗證端點回傳 HTTP 狀態碼不是 200,或者在解析回傳的 JSON 時發生問題時,將會執行 error 方法。

廣播至 Presence Channels

Presence 頻道可以像公開或私有頻道一樣接收事件。以聊天室為例,我們可能想要將 NewMessage 事件廣播到房間的 presence 頻道。為此,我們將從事件的 broadcastOn 方法回傳 PresenceChannel 的實例。

1/**
2 * Get the channels the event should broadcast on.
3 *
4 * @return array<int, \Illuminate\Broadcasting\Channel>
5 */
6public function broadcastOn(): array
7{
8 return [
9 new PresenceChannel('chat.'.$this->message->room_id),
10 ];
11}

與其他事件一樣,您可以使用 broadcast 輔助函數和 toOthers 方法,將目前使用者排除在接收廣播之外。

1broadcast(new NewMessage($message));
2 
3broadcast(new NewMessage($message))->toOthers();

與其他類型的事件一樣,您可以使用 Echo 的 listen 方法來監聽傳送到 presence 頻道的事件。

1Echo.join(`chat.${roomId}`)
2 .here(/* ... */)
3 .joining(/* ... */)
4 .leaving(/* ... */)
5 .listen('NewMessage', (e) => {
6 // ...
7 });

模型廣播

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

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

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

若要開始使用,您的 Eloquent 模型應使用 Illuminate\Database\Eloquent\BroadcastsEvents trait。此外,模型應定義 broadcastOn 方法,該方法將回傳一個陣列,其中包含模型事件應廣播到的頻道。

1<?php
2 
3namespace App\Models;
4 
5use Illuminate\Broadcasting\Channel;
6use Illuminate\Broadcasting\PrivateChannel;
7use Illuminate\Database\Eloquent\BroadcastsEvents;
8use Illuminate\Database\Eloquent\Factories\HasFactory;
9use Illuminate\Database\Eloquent\Model;
10use Illuminate\Database\Eloquent\Relations\BelongsTo;
11 
12class Post extends Model
13{
14 use BroadcastsEvents, HasFactory;
15 
16 /**
17 * Get the user that the post belongs to.
18 */
19 public function user(): BelongsTo
20 {
21 return $this->belongsTo(User::class);
22 }
23 
24 /**
25 * Get the channels that model events should broadcast on.
26 *
27 * @return array<int, \Illuminate\Broadcasting\Channel|\Illuminate\Database\Eloquent\Model>
28 */
29 public function broadcastOn(string $event): array
30 {
31 return [$this, $this->user];
32 }
33}

一旦您的模型包含此 trait 並定義其廣播頻道,它將開始在模型實例被建立、更新、刪除、軟刪除或還原時自動廣播事件。

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

1/**
2 * Get the channels that model events should broadcast on.
3 *
4 * @return array<string, array<int, \Illuminate\Broadcasting\Channel|\Illuminate\Database\Eloquent\Model>>
5 */
6public function broadcastOn(string $event): array
7{
8 return match ($event) {
9 'deleted' => [],
10 default => [$this, $this->user],
11 };
12}

自訂模型廣播事件建立

有時,您可能希望自訂 Laravel 如何建立底層模型廣播事件。您可以透過在您的 Eloquent 模型上定義 newBroadcastableEvent 方法來完成此操作。此方法應回傳一個 Illuminate\Database\Eloquent\BroadcastableModelEventOccurred 實例。

1use Illuminate\Database\Eloquent\BroadcastableModelEventOccurred;
2 
3/**
4 * Create a new broadcastable model event for the model.
5 */
6protected function newBroadcastableEvent(string $event): BroadcastableModelEventOccurred
7{
8 return (new BroadcastableModelEventOccurred(
9 $this, $event
10 ))->dontBroadcastToCurrentUser();
11}

模型廣播慣例

頻道慣例

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

因此,具有 id1App\Models\User 模型將被轉換為名稱為 App.Models.User.1Illuminate\Broadcasting\PrivateChannel 實例。當然,除了從模型的 broadcastOn 方法回傳 Eloquent 模型實例之外,您還可以回傳完整的 Channel 實例,以便完全控制模型的頻道名稱。

1use Illuminate\Broadcasting\PrivateChannel;
2 
3/**
4 * Get the channels that model events should broadcast on.
5 *
6 * @return array<int, \Illuminate\Broadcasting\Channel>
7 */
8public function broadcastOn(string $event): array
9{
10 return [
11 new PrivateChannel('user.'.$this->id)
12 ];
13}

如果您計劃從模型的 broadcastOn 方法明確回傳頻道實例,您可以將 Eloquent 模型實例傳遞給頻道的建構子。這樣做時,Laravel 將使用上面討論的模型頻道慣例,將 Eloquent 模型轉換為頻道名稱字串。

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

如果您需要確定模型的頻道名稱,您可以對任何模型實例呼叫 broadcastChannel 方法。例如,對於具有 id1App\Models\User 模型,此方法會回傳字串 App.Models.User.1

1$user->broadcastChannel()

事件慣例

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

因此,例如,對 App\Models\Post 模型的更新將向您的客戶端應用程式廣播一個名為 PostUpdated 的事件,並帶有以下有效負載:

1{
2 "model": {
3 "id": 1,
4 "title": "My first post"
5 ...
6 },
7 ...
8 "socket": "someSocketId",
9}

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

如果您願意,您可以透過在模型中新增 broadcastAsbroadcastWith 方法來定義自訂廣播名稱和有效負載。這些方法接收正在發生的模型事件/操作的名稱,讓您可以自訂每個模型操作的事件名稱和有效負載。如果從 broadcastAs 方法回傳 null,Laravel 將在使用模型廣播事件名稱慣例廣播事件時。

1/**
2 * The model event's broadcast name.
3 */
4public function broadcastAs(string $event): string|null
5{
6 return match ($event) {
7 'created' => 'post.created',
8 default => null,
9 };
10}
11 
12/**
13 * Get the data to broadcast for the model.
14 *
15 * @return array<string, mixed>
16 */
17public function broadcastWith(string $event): array
18{
19 return match ($event) {
20 'created' => ['title' => $this->title],
21 default => ['model' => $this],
22 };
23}

監聽模型廣播

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

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

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

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

客戶端事件

當使用 Pusher Channels 時,您必須在您的 應用程式儀表板的「App Settings」區段中啟用「Client Events」選項,才能發送客戶端事件。

有時您可能希望將事件廣播到其他連線的客戶端,而完全不訪問您的 Laravel 應用程式。這對於諸如「正在輸入」通知之類的事情特別有用,在這種情況下,您希望提醒您的應用程式使用者,另一個使用者正在給定的螢幕上輸入訊息。

若要廣播客戶端事件,您可以使用 Echo 的 whisper 方法。

1Echo.private(`chat.${roomId}`)
2 .whisper('typing', {
3 name: this.user.name
4 });

若要監聽客戶端事件,您可以使用 listenForWhisper 方法。

1Echo.private(`chat.${roomId}`)
2 .listenForWhisper('typing', (e) => {
3 console.log(e.name);
4 });

通知

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

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

1Echo.private(`App.Models.User.${userId}`)
2 .notification((notification) => {
3 console.log(notification.type);
4 });

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

Laravel 是最有效率的方式來
建構、部署和監控軟體。