廣播
簡介
在許多現代的網路應用程式中,WebSockets 用於實現即時、動態更新的使用者介面。當伺服器上的某些資料更新時,通常會透過 WebSocket 連線傳送訊息,以便由客戶端處理。WebSockets 提供了一種更有效率的替代方案,可以持續輪詢應用程式的伺服器,以獲取應反映在 UI 中的資料變更。
例如,假設您的應用程式可以將使用者的資料匯出到 CSV 檔案並以電子郵件寄送給他們。但是,建立此 CSV 檔案需要幾分鐘的時間,因此您選擇在佇列工作中建立並寄送 CSV。當 CSV 建立並寄送給使用者後,我們可以使用事件廣播來發送一個 App\Events\UserDataExported
事件,該事件會由我們應用程式的 JavaScript 接收。一旦收到事件,我們可以向使用者顯示訊息,告知他們 CSV 已透過電子郵件寄送給他們,而無需重新整理頁面。
為了協助您建構這些類型的功能,Laravel 可以輕鬆地透過 WebSocket 連線「廣播」伺服器端的 Laravel 事件。廣播您的 Laravel 事件可讓您在伺服器端 Laravel 應用程式和客戶端 JavaScript 應用程式之間共用相同的事件名稱和資料。
廣播背後的中心概念很簡單:客戶端連線到前端的具名頻道,而您的 Laravel 應用程式則將事件廣播到後端的這些頻道。這些事件可以包含您希望提供給前端的任何其他資料。
支援的驅動程式
預設情況下,Laravel 包含三個伺服器端廣播驅動程式供您選擇:Laravel Reverb、Pusher Channels 和 Ably。
在深入了解事件廣播之前,請務必閱讀 Laravel 關於事件和監聽器的文件。
伺服器端安裝
若要開始使用 Laravel 的事件廣播,我們需要在 Laravel 應用程式中進行一些設定,並安裝幾個套件。
事件廣播是透過伺服器端廣播驅動程式完成的,該驅動程式會廣播您的 Laravel 事件,以便 Laravel Echo (一個 JavaScript 程式庫) 可以在瀏覽器用戶端中接收它們。別擔心 - 我們將逐步完成安裝過程的每個部分。
設定
您應用程式的所有事件廣播設定都儲存在 config/broadcasting.php
設定檔中。如果您的應用程式中不存在此目錄,請別擔心;當您執行 install:broadcasting
Artisan 命令時,將會建立此目錄。
Laravel 支援多種開箱即用的廣播驅動程式:Laravel Reverb、Pusher Channels、Ably,以及用於本機開發和偵錯的 log
驅動程式。此外,還包含一個 null
驅動程式,可讓您在測試期間停用廣播。config/broadcasting.php
設定檔中包含每個驅動程式的設定範例。
安裝
預設情況下,新的 Laravel 應用程式中不會啟用廣播。您可以使用 install:broadcasting
Artisan 命令啟用廣播
php artisan install:broadcasting
install:broadcasting
命令將建立 config/broadcasting.php
設定檔。此外,該命令將建立 routes/channels.php
檔案,您可以在其中註冊應用程式的廣播授權路由和回呼。
佇列設定
在廣播任何事件之前,您應先設定並執行佇列工作程序。所有事件廣播都是透過佇列工作來完成的,這樣您的應用程式的回應時間才不會受到廣播事件的嚴重影響。
Reverb
執行 install:broadcasting
命令時,系統會提示您安裝 Laravel Reverb。當然,您也可以使用 Composer 套件管理員手動安裝 Reverb。
composer require laravel/reverb
安裝套件後,您可以執行 Reverb 的安裝命令來發佈設定、新增 Reverb 所需的環境變數,並在您的應用程式中啟用事件廣播
php artisan reverb:install
您可以在Reverb 文件中找到詳細的 Reverb 安裝和使用說明。
Pusher Channels
如果您計畫使用 Pusher Channels 廣播您的事件,您應使用 Composer 套件管理員安裝 Pusher Channels PHP SDK
composer require pusher/pusher-php-server
接下來,您應在 config/broadcasting.php
設定檔中設定您的 Pusher Channels 認證。此檔案中已包含 Pusher Channels 設定範例,讓您可以快速指定您的金鑰、密碼和應用程式 ID。一般而言,您應在應用程式的 .env
檔案中設定 Pusher Channels 認證
PUSHER_APP_ID="your-pusher-app-id"PUSHER_APP_KEY="your-pusher-key"PUSHER_APP_SECRET="your-pusher-secret"PUSHER_HOST=PUSHER_PORT=443PUSHER_SCHEME="https"PUSHER_APP_CLUSTER="mt1"
config/broadcasting.php
檔案的 pusher
設定也允許您指定 Channels 支援的其他 options
,例如叢集。
然後,在您應用程式的 .env
檔案中,將 BROADCAST_CONNECTION
環境變數設定為 pusher
BROADCAST_CONNECTION=pusher
最後,您已準備好安裝和設定Laravel Echo,它將在用戶端接收廣播事件。
Ably
以下文件討論如何在「Pusher 相容性」模式下使用 Ably。但是,Ably 團隊建議並維護一個廣播器和 Echo 用戶端,它可以利用 Ably 提供的獨特功能。有關使用 Ably 維護的驅動程式的詳細資訊,請參閱 Ably 的 Laravel 廣播器文件。
如果您計畫使用 Ably 廣播您的事件,您應使用 Composer 套件管理員安裝 Ably PHP SDK
composer require ably/ably-php
接下來,您應在 config/broadcasting.php
設定檔中設定您的 Ably 認證。此檔案中已包含 Ably 設定範例,讓您可以快速指定您的金鑰。一般而言,此值應透過 ABLY_KEY
環境變數設定
ABLY_KEY=your-ably-key
然後,在您應用程式的 .env
檔案中,將 BROADCAST_CONNECTION
環境變數設定為 ably
BROADCAST_CONNECTION=ably
最後,您已準備好安裝和設定Laravel Echo,它將在用戶端接收廣播事件。
客戶端安裝
Reverb
Laravel Echo 是一個 JavaScript 程式庫,可讓您輕鬆訂閱頻道並監聽伺服器端廣播驅動程式廣播的事件。您可以透過 NPM 套件管理員安裝 Echo。在本範例中,我們也將安裝 pusher-js
套件,因為 Reverb 使用 Pusher 協定來進行 WebSocket 訂閱、頻道和訊息
npm install --save-dev laravel-echo pusher-js
一旦安裝 Echo 後,您就可以在應用程式的 JavaScript 中建立一個新的 Echo 實例。一個很好的位置是在 Laravel 框架中包含的 resources/js/bootstrap.js
檔案底部。預設情況下,此檔案中已包含一個範例 Echo 設定 - 您只需取消註解它,並將 broadcaster
設定選項更新為 reverb
。
import Echo from 'laravel-echo'; import Pusher from 'pusher-js';window.Pusher = Pusher; window.Echo = new Echo({ broadcaster: 'reverb', key: import.meta.env.VITE_REVERB_APP_KEY, wsHost: import.meta.env.VITE_REVERB_HOST, wsPort: import.meta.env.VITE_REVERB_PORT, wssPort: import.meta.env.VITE_REVERB_PORT, forceTLS: (import.meta.env.VITE_REVERB_SCHEME ?? 'https') === 'https', enabledTransports: ['ws', 'wss'],});
接下來,您應該編譯應用程式的資源。
npm run build
Laravel Echo 的 reverb
廣播器需要 laravel-echo v1.16.0 以上版本。
Pusher Channels
Laravel Echo 是一個 JavaScript 函式庫,可讓您輕鬆訂閱頻道並監聽伺服器端廣播驅動程式廣播的事件。Echo 還利用 pusher-js
NPM 套件來實作 Pusher 通訊協定,用於 WebSocket 訂閱、頻道和訊息。
install:broadcasting
Artisan 命令會自動為您安裝 laravel-echo
和 pusher-js
套件;但是,您也可以透過 NPM 手動安裝這些套件。
npm install --save-dev laravel-echo pusher-js
一旦安裝 Echo 後,您就可以在應用程式的 JavaScript 中建立一個新的 Echo 實例。 install:broadcasting
命令會在 resources/js/echo.js
建立一個 Echo 設定檔;但是,此檔案中的預設設定適用於 Laravel Reverb。您可以複製以下設定,將您的設定轉換為 Pusher。
import Echo from 'laravel-echo'; import Pusher from 'pusher-js';window.Pusher = Pusher; window.Echo = new Echo({ broadcaster: 'pusher', key: import.meta.env.VITE_PUSHER_APP_KEY, cluster: import.meta.env.VITE_PUSHER_APP_CLUSTER, forceTLS: true});
接下來,您應該在應用程式的 .env
檔案中定義 Pusher 環境變數的適當值。如果這些變數尚未存在於您的 .env
檔案中,您應該新增它們。
PUSHER_APP_ID="your-pusher-app-id"PUSHER_APP_KEY="your-pusher-key"PUSHER_APP_SECRET="your-pusher-secret"PUSHER_HOST=PUSHER_PORT=443PUSHER_SCHEME="https"PUSHER_APP_CLUSTER="mt1" VITE_APP_NAME="${APP_NAME}"VITE_PUSHER_APP_KEY="${PUSHER_APP_KEY}"VITE_PUSHER_HOST="${PUSHER_HOST}"VITE_PUSHER_PORT="${PUSHER_PORT}"VITE_PUSHER_SCHEME="${PUSHER_SCHEME}"VITE_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
一旦您根據應用程式的需求調整了 Echo 設定,您就可以編譯應用程式的資源。
npm run build
若要進一步了解如何編譯應用程式的 JavaScript 資源,請參閱關於 Vite 的文件。
使用現有的客戶端實例
如果您已經有一個預先設定好的 Pusher Channels 客戶端實例,並且希望 Echo 使用它,您可以透過 client
設定選項將它傳遞給 Echo。
import Echo from 'laravel-echo';import Pusher from 'pusher-js'; const options = { broadcaster: 'pusher', key: 'your-pusher-channels-key'} window.Echo = new Echo({ ...options, client: new Pusher(options.key, options)});
Ably
以下文件討論如何在「Pusher 相容性」模式下使用 Ably。但是,Ably 團隊建議並維護一個廣播器和 Echo 用戶端,它可以利用 Ably 提供的獨特功能。有關使用 Ably 維護的驅動程式的詳細資訊,請參閱 Ably 的 Laravel 廣播器文件。
Laravel Echo 是一個 JavaScript 函式庫,可讓您輕鬆訂閱頻道並監聽伺服器端廣播驅動程式廣播的事件。Echo 還利用 pusher-js
NPM 套件來實作 Pusher 通訊協定,用於 WebSocket 訂閱、頻道和訊息。
install:broadcasting
Artisan 命令會自動為您安裝 laravel-echo
和 pusher-js
套件;但是,您也可以透過 NPM 手動安裝這些套件。
npm install --save-dev laravel-echo pusher-js
在繼續之前,您應該在 Ably 應用程式設定中啟用 Pusher 通訊協定支援。您可以在 Ably 應用程式設定儀表板的「通訊協定轉接器設定」部分中啟用此功能。
一旦安裝 Echo 後,您就可以在應用程式的 JavaScript 中建立一個新的 Echo 實例。 install:broadcasting
命令會在 resources/js/echo.js
建立一個 Echo 設定檔;但是,此檔案中的預設設定適用於 Laravel Reverb。您可以複製以下設定,將您的設定轉換為 Ably。
import Echo from 'laravel-echo'; import Pusher from 'pusher-js';window.Pusher = Pusher; window.Echo = new Echo({ broadcaster: 'pusher', key: import.meta.env.VITE_ABLY_PUBLIC_KEY, wsHost: 'realtime-pusher.ably.io', wsPort: 443, disableStats: true, encrypted: true,});
您可能已經注意到我們的 Ably Echo 設定參考了一個 VITE_ABLY_PUBLIC_KEY
環境變數。此變數的值應該是您的 Ably 公開金鑰。您的公開金鑰是 Ably 金鑰中 :
字元之前的部分。
一旦您根據需求調整了 Echo 設定,您就可以編譯應用程式的資源。
npm run dev
若要進一步了解如何編譯應用程式的 JavaScript 資源,請參閱關於 Vite 的文件。
概念概述
Laravel 的事件廣播允許您使用基於驅動程式的 WebSockets 方法將伺服器端 Laravel 事件廣播到您的客戶端 JavaScript 應用程式。目前,Laravel 隨附 Pusher Channels 和 Ably 驅動程式。可以使用 Laravel Echo JavaScript 套件輕鬆在客戶端使用這些事件。
事件透過「頻道」廣播,這些頻道可以指定為公開或私有。任何訪問您應用程式的使用者都可以訂閱公開頻道,而無需任何驗證或授權;但是,為了訂閱私有頻道,使用者必須經過驗證並授權才能收聽該頻道。
使用範例應用程式
在深入研究事件廣播的每個元件之前,讓我們先以電子商務商店為例,進行高階概觀。
在我們的應用程式中,假設我們有一個頁面允許使用者查看其訂單的運送狀態。我們也假設當應用程式處理運送狀態更新時,會觸發一個 OrderShipmentStatusUpdated
事件。
use App\Events\OrderShipmentStatusUpdated; OrderShipmentStatusUpdated::dispatch($order);
ShouldBroadcast
介面
當使用者正在查看他們的其中一個訂單時,我們不希望他們必須重新整理頁面才能查看狀態更新。相反地,我們希望在建立更新時將更新廣播到應用程式。因此,我們需要使用 ShouldBroadcast
介面標記 OrderShipmentStatusUpdated
事件。這會指示 Laravel 在觸發事件時廣播該事件。
<?php namespace App\Events; use App\Models\Order;use Illuminate\Broadcasting\Channel;use Illuminate\Broadcasting\InteractsWithSockets;use Illuminate\Broadcasting\PresenceChannel;use Illuminate\Contracts\Broadcasting\ShouldBroadcast;use Illuminate\Queue\SerializesModels; class OrderShipmentStatusUpdated implements ShouldBroadcast{ /** * The order instance. * * @var \App\Models\Order */ public $order;}
ShouldBroadcast
介面要求我們的事件定義一個 broadcastOn
方法。此方法負責返回事件應廣播到的頻道。產生的事件類別中已經定義了此方法的空存根,因此我們只需要填寫其詳細資訊即可。我們只希望訂單的建立者能夠查看狀態更新,因此我們將在與訂單相關的私有頻道上廣播該事件。
use Illuminate\Broadcasting\Channel;use Illuminate\Broadcasting\PrivateChannel; /** * Get the channel the event should broadcast on. */public function broadcastOn(): Channel{ return new PrivateChannel('orders.'.$this->order->id);}
如果您希望事件在多個頻道上廣播,您可以返回一個 array
。
use Illuminate\Broadcasting\PrivateChannel; /** * Get the channels the event should broadcast on. * * @return array<int, \Illuminate\Broadcasting\Channel> */public function broadcastOn(): array{ return [ new PrivateChannel('orders.'.$this->order->id), // ... ];}
授權頻道
請記住,使用者必須經過授權才能收聽私有頻道。我們可以在應用程式的 routes/channels.php
檔案中定義我們的頻道授權規則。在此範例中,我們需要驗證任何嘗試收聽私有 orders.1
頻道的使用者實際上是否為訂單的建立者。
use App\Models\Order;use App\Models\User; Broadcast::channel('orders.{orderId}', function (User $user, int $orderId) { return $user->id === Order::findOrNew($orderId)->user_id;});
channel
方法接受兩個參數:頻道名稱和一個回呼函數,該函數返回 true
或 false
,表示使用者是否已授權收聽該頻道。
所有授權回呼函數都會接收目前已驗證的使用者作為第一個參數,並接收任何其他萬用字元參數作為後續參數。在此範例中,我們使用 {orderId}
佔位符來表示頻道名稱的「ID」部分是萬用字元。
監聽事件廣播
接下來,剩下的就是在我們的 JavaScript 應用程式中監聽事件。我們可以使用 Laravel Echo 來完成此操作。首先,我們將使用 private
方法訂閱私有頻道。然後,我們可以使用 listen
方法來監聽 OrderShipmentStatusUpdated
事件。預設情況下,所有事件的公開屬性都會包含在廣播事件中。
Echo.private(`orders.${orderId}`) .listen('OrderShipmentStatusUpdated', (e) => { console.log(e.order); });
定義廣播事件
若要通知 Laravel 應廣播給定的事件,您必須在事件類別上實作 Illuminate\Contracts\Broadcasting\ShouldBroadcast
介面。此介面已匯入框架產生的所有事件類別,因此您可以輕鬆地將其新增至任何事件。
ShouldBroadcast
介面要求您實作一個方法:broadcastOn
。broadcastOn
方法應該返回事件應該廣播到的頻道或頻道陣列。這些頻道應該是 Channel
、PrivateChannel
或 PresenceChannel
的實例。Channel
的實例代表任何使用者都可以訂閱的公開頻道,而 PrivateChannels
和 PresenceChannels
則代表需要頻道授權的私有頻道。
<?php namespace App\Events; use App\Models\User;use Illuminate\Broadcasting\Channel;use Illuminate\Broadcasting\InteractsWithSockets;use Illuminate\Broadcasting\PresenceChannel;use Illuminate\Broadcasting\PrivateChannel;use Illuminate\Contracts\Broadcasting\ShouldBroadcast;use Illuminate\Queue\SerializesModels; class ServerCreated implements ShouldBroadcast{ use SerializesModels; /** * Create a new event instance. */ public function __construct( public User $user, ) {} /** * Get the channels the event should broadcast on. * * @return array<int, \Illuminate\Broadcasting\Channel> */ public function broadcastOn(): array { return [ new PrivateChannel('user.'.$this->user->id), ]; }}
實作 ShouldBroadcast
介面後,您只需要像往常一樣觸發事件即可。一旦觸發事件,排隊作業將自動使用您指定的廣播驅動程式廣播該事件。
廣播名稱
預設情況下,Laravel 將使用事件的類別名稱廣播事件。但是,您可以透過在事件上定義 broadcastAs
方法來自訂廣播名稱。
/** * The event's broadcast name. */public function broadcastAs(): string{ return 'server.created';}
如果您使用 broadcastAs
方法來自訂廣播名稱,您應該確保以開頭的 .
字元註冊您的監聽器。這會指示 Echo 不要將應用程式的命名空間附加到事件。
.listen('.server.created', function (e) { ....});
廣播資料
當事件廣播時,其所有 public
屬性都會自動序列化並廣播為事件的酬載,讓您可以從 JavaScript 應用程式存取其任何公開資料。因此,舉例來說,如果您的事件有一個包含 Eloquent 模型的單一公開 $user
屬性,則事件的廣播酬載將會是
{ "user": { "id": 1, "name": "Patrick Stewart" ... }}
但是,如果您希望對廣播酬載有更精細的控制,您可以將 broadcastWith
方法新增至您的事件。此方法應該返回您希望廣播為事件酬載的資料陣列。
/** * Get the data to broadcast. * * @return array<string, mixed> */public function broadcastWith(): array{ return ['id' => $this->user->id];}
廣播佇列
預設情況下,每個廣播事件都會放置在 queue.php
設定檔中指定的預設佇列連線的預設佇列上。您可以透過在事件類別上定義 connection
和 queue
屬性來自訂廣播器使用的佇列連線和名稱。
/** * The name of the queue connection to use when broadcasting the event. * * @var string */public $connection = 'redis'; /** * The name of the queue on which to place the broadcasting job. * * @var string */public $queue = 'default';
或者,您可以透過在事件上定義 broadcastQueue
方法來自訂佇列名稱。
/** * The name of the queue on which to place the broadcasting job. */public function broadcastQueue(): string{ return 'default';}
如果您希望使用 sync
佇列而非預設佇列驅動程式廣播事件,您可以實作 ShouldBroadcastNow
介面而非 ShouldBroadcast
。
<?php use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow; class OrderShipmentStatusUpdated implements ShouldBroadcastNow{ // ...}
廣播條件
有時,您只希望在給定條件為 true 時廣播事件。您可以透過將 broadcastWhen
方法新增至您的事件類別來定義這些條件。
/** * Determine if this event should broadcast. */public function broadcastWhen(): bool{ return $this->order->value > 100;}
廣播和資料庫交易
當在資料庫交易中分派廣播事件時,它們可能會在資料庫交易提交之前由佇列處理。當這種情況發生時,您在資料庫交易期間對模型或資料庫記錄所做的任何更新可能尚未反映在資料庫中。此外,在交易中建立的任何模型或資料庫記錄可能不存在於資料庫中。如果您的事件依賴於這些模型,則在處理廣播事件的作業時可能會發生意外錯誤。
如果您的佇列連線的 after_commit
設定選項設定為 false
,您仍然可以透過在事件類別上實作 ShouldDispatchAfterCommit
介面來指出特定的廣播事件應在所有開啟的資料庫交易提交後分派。
<?php namespace App\Events; use Illuminate\Contracts\Broadcasting\ShouldBroadcast;use Illuminate\Contracts\Events\ShouldDispatchAfterCommit;use Illuminate\Queue\SerializesModels; class ServerCreated implements ShouldBroadcast, ShouldDispatchAfterCommit{ use SerializesModels;}
若要深入了解如何解決這些問題,請檢閱關於排隊作業和資料庫交易的文件。
授權頻道
私有頻道要求您授權目前已驗證的使用者實際上可以收聽該頻道。這是透過向您的 Laravel 應用程式發出包含頻道名稱的 HTTP 請求,並允許您的應用程式判斷使用者是否可以收聽該頻道來完成的。使用 Laravel Echo 時,將自動發出 HTTP 請求以授權訂閱私有頻道。
啟用廣播時,Laravel 會自動註冊 /broadcasting/auth
路由來處理授權請求。 /broadcasting/auth
路由會自動放置在 web
中介層群組中。
定義授權回呼
接下來,我們需要定義實際上將判斷目前已驗證的使用者是否可以收聽給定頻道的邏輯。這是在 install:broadcasting
Artisan 命令建立的 routes/channels.php
檔案中完成的。在此檔案中,您可以使用 Broadcast::channel
方法來註冊頻道授權回呼函數。
use App\Models\User; Broadcast::channel('orders.{orderId}', function (User $user, int $orderId) { return $user->id === Order::findOrNew($orderId)->user_id;});
channel
方法接受兩個參數:頻道名稱和一個回呼函數,該函數返回 true
或 false
,表示使用者是否已授權收聽該頻道。
所有授權回呼函數都會接收目前已驗證的使用者作為第一個參數,並接收任何其他萬用字元參數作為後續參數。在此範例中,我們使用 {orderId}
佔位符來表示頻道名稱的「ID」部分是萬用字元。
您可以使用 channel:list
Artisan 命令查看應用程式的廣播授權回呼函數清單。
php artisan channel:list
授權回呼函數模型繫結
如同 HTTP 路由一樣,通道路由也可以利用隱式和顯式的路由模型綁定。例如,你可以請求一個實際的 Order
模型實例,而不是接收字串或數字的訂單 ID。
use App\Models\Order;use App\Models\User; Broadcast::channel('orders.{order}', function (User $user, Order $order) { return $user->id === $order->user_id;});
與 HTTP 路由模型綁定不同,通道模型綁定不支援自動隱式模型綁定作用域。然而,這很少會是問題,因為大多數通道都可以根據單一模型的唯一主鍵來定義作用域。
授權回調驗證
私有和出席廣播通道會透過應用程式的預設驗證守衛來驗證當前使用者。如果使用者未經驗證,則會自動拒絕通道授權,且永遠不會執行授權回調。但是,如果需要,您可以指定多個自訂守衛來驗證傳入的請求。
Broadcast::channel('channel', function () { // ...}, ['guards' => ['web', 'admin']]);
定義頻道類別
如果您的應用程式正在使用許多不同的通道,則您的 routes/channels.php
檔案可能會變得臃腫。因此,您可以使用通道類別,而不是使用閉包來授權通道。要產生通道類別,請使用 make:channel
Artisan 命令。此命令會在 App/Broadcasting
目錄中放置一個新的通道類別。
php artisan make:channel OrderChannel
接下來,在您的 routes/channels.php
檔案中註冊您的通道。
use App\Broadcasting\OrderChannel; Broadcast::channel('orders.{order}', OrderChannel::class);
最後,您可以將通道的授權邏輯放置在通道類別的 join
方法中。此 join
方法會包含您通常會放置在通道授權閉包中的相同邏輯。您也可以利用通道模型綁定。
<?php namespace App\Broadcasting; use App\Models\Order;use App\Models\User; class OrderChannel{ /** * Create a new channel instance. */ public function __construct() {} /** * Authenticate the user's access to the channel. */ public function join(User $user, Order $order): array|bool { return $user->id === $order->user_id; }}
與 Laravel 中的許多其他類別一樣,通道類別將由服務容器自動解析。因此,您可以在其建構函式中型別提示通道所需的任何相依性。
廣播事件
一旦您定義了一個事件並使用 ShouldBroadcast
介面標記它,您只需要使用事件的 dispatch 方法觸發該事件。事件派發器會注意到該事件已使用 ShouldBroadcast
介面標記,並將該事件排入廣播佇列。
use App\Events\OrderShipmentStatusUpdated; OrderShipmentStatusUpdated::dispatch($order);
僅對其他人廣播
在建構利用事件廣播的應用程式時,您可能偶爾需要向給定通道的所有訂閱者廣播事件,但不包括當前使用者。您可以使用 broadcast
輔助函式和 toOthers
方法來完成此操作。
use App\Events\OrderShipmentStatusUpdated; broadcast(new OrderShipmentStatusUpdated($update))->toOthers();
為了更好地了解何時可能需要使用 toOthers
方法,讓我們想像一個任務清單應用程式,使用者可以在其中輸入任務名稱來建立新任務。若要建立任務,您的應用程式可能會向 /task
URL 發出請求,該請求會廣播任務的建立並返回新任務的 JSON 表示。當您的 JavaScript 應用程式收到端點的回應時,它可能會像這樣直接將新任務插入其任務清單中。
axios.post('/task', task) .then((response) => { this.tasks.push(response.data); });
但是,請記住,我們也會廣播任務的建立。如果您的 JavaScript 應用程式也正在監聽此事件,以便將任務新增至任務清單,則您的清單中會有重複的任務:一個來自端點,一個來自廣播。您可以使用 toOthers
方法來解決此問題,以指示廣播器不要將事件廣播給當前使用者。
您的事件必須使用 Illuminate\Broadcasting\InteractsWithSockets
特徵才能呼叫 toOthers
方法。
設定
當您初始化 Laravel Echo 實例時,會為連線指派一個 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();
自訂連線
如果您的應用程式與多個廣播連線互動,並且您想要使用預設以外的廣播器廣播事件,則可以使用 via
方法指定要將事件推送到的連線。
use App\Events\OrderShipmentStatusUpdated; broadcast(new OrderShipmentStatusUpdated($update))->via('pusher');
或者,您可以在事件的建構函式中呼叫 broadcastVia
方法來指定事件的廣播連線。但是,在執行此操作之前,您應確保事件類別使用 InteractsWithBroadcasting
特徵。
<?php namespace App\Events; use Illuminate\Broadcasting\Channel;use Illuminate\Broadcasting\InteractsWithBroadcasting;use Illuminate\Broadcasting\InteractsWithSockets;use Illuminate\Broadcasting\PresenceChannel;use Illuminate\Broadcasting\PrivateChannel;use Illuminate\Contracts\Broadcasting\ShouldBroadcast;use Illuminate\Queue\SerializesModels; class OrderShipmentStatusUpdated implements ShouldBroadcast{ use InteractsWithBroadcasting; /** * Create a new event instance. */ public function __construct() { $this->broadcastVia('pusher'); }}
匿名事件
有時,您可能想要將簡單的事件廣播到應用程式的前端,而無需建立專用的事件類別。為了滿足此需求,Broadcast
外觀允許您廣播「匿名事件」。
Broadcast::on('orders.'.$order->id)->send();
上面的範例會廣播以下事件
{ "event": "AnonymousEvent", "data": "[]", "channel": "orders.1"}
使用 as
和 with
方法,您可以自訂事件的名稱和資料。
Broadcast::on('orders.'.$order->id) ->as('OrderPlaced') ->with($order) ->send();
上面的範例會廣播類似以下的事件
{ "event": "OrderPlaced", "data": "{ id: 1, total: 100 }", "channel": "orders.1"}
如果您想要在私有或出席通道上廣播匿名事件,則可以使用 private
和 presence
方法。
Broadcast::private('orders.'.$order->id)->send();Broadcast::presence('channels.'.$channel->id)->send();
使用 send
方法廣播匿名事件會將事件派發到應用程式的佇列以進行處理。但是,如果您想要立即廣播事件,則可以使用 sendNow
方法。
Broadcast::on('orders.'.$order->id)->sendNow();
若要將事件廣播給除目前經過驗證的使用者之外的所有通道訂閱者,您可以調用 toOthers
方法。
Broadcast::on('orders.'.$order->id) ->toOthers() ->send();
接收廣播
監聽事件
一旦您安裝並實例化 Laravel Echo,您就可以開始監聽從 Laravel 應用程式廣播的事件。首先,使用 channel
方法來擷取通道的實例,然後呼叫 listen
方法來監聽指定的事件。
Echo.channel(`orders.${this.order.id}`) .listen('OrderShipmentStatusUpdated', (e) => { console.log(e.order.name); });
如果您想要監聽私有通道上的事件,請改用 private
方法。您可以繼續鏈式呼叫 listen
方法,以監聽單一通道上的多個事件。
Echo.private(`orders.${this.order.id}`) .listen(/* ... */) .listen(/* ... */) .listen(/* ... */);
停止監聽事件
如果您想要停止監聽給定的事件而無需離開通道,則可以使用 stopListening
方法。
Echo.private(`orders.${this.order.id}`) .stopListening('OrderShipmentStatusUpdated')
離開頻道
若要離開通道,您可以在 Echo 實例上呼叫 leaveChannel
方法。
Echo.leaveChannel(`orders.${this.order.id}`);
如果您想要離開通道及其關聯的私有和出席通道,則可以呼叫 leave
方法。
Echo.leave(`orders.${this.order.id}`);
命名空間
您可能已經注意到在上面的範例中,我們沒有指定事件類別的完整 App\Events
命名空間。這是因為 Echo 會自動假設事件位於 App\Events
命名空間中。但是,您可以在實例化 Echo 時傳遞 namespace
設定選項來設定根命名空間。
window.Echo = new Echo({ broadcaster: 'pusher', // ... namespace: 'App.Other.Namespace'});
或者,您可以使用 Echo 訂閱事件時,以 .
為字首加上事件類別。這將允許您始終指定完整限定的類別名稱。
Echo.channel('orders') .listen('.Namespace\\Event\\Class', (e) => { // ... });
在線頻道
出席通道建立在私有通道的安全性之上,同時公開了額外的功能,即了解誰已訂閱該通道。這使得建構強大的協作應用程式功能變得容易,例如當另一個使用者正在檢視同一頁面或列出聊天室的成員時通知使用者。
授權在線頻道
所有出席通道也是私有通道;因此,使用者必須獲得授權才能存取它們。但是,當為出席通道定義授權回調時,如果使用者已獲得授權可以加入通道,您不會傳回 true
。相反地,您應該傳回一個關於使用者的資料陣列。
授權回調傳回的資料將在您的 JavaScript 應用程式中提供給出席通道事件監聽器。如果使用者未獲得授權可以加入出席通道,您應該傳回 false
或 null
。
use App\Models\User; Broadcast::channel('chat.{roomId}', function (User $user, int $roomId) { if ($user->canJoinRoom($roomId)) { return ['id' => $user->id, 'name' => $user->name]; }});
加入在線頻道
若要加入出席通道,您可以使用 Echo 的 join
方法。join
方法會傳回 PresenceChannel
實作,該實作除了公開 listen
方法之外,還允許您訂閱 here
、joining
和 leaving
事件。
Echo.join(`chat.${roomId}`) .here((users) => { // ... }) .joining((user) => { console.log(user.name); }) .leaving((user) => { console.log(user.name); }) .error((error) => { console.error(error); });
here
回調會在成功加入通道後立即執行,並會收到一個包含目前訂閱通道的所有其他使用者資訊的陣列。當新使用者加入通道時,會執行 joining
方法,而當使用者離開通道時,則會執行 leaving
方法。當驗證端點傳回 HTTP 狀態碼 200 以外的值,或在剖析傳回的 JSON 時發生問題時,則會執行 error
方法。
廣播至在線頻道
出席通道可以像公開或私有通道一樣接收事件。使用聊天室的範例,我們可能想要向聊天室的出席通道廣播 NewMessage
事件。若要執行此操作,我們將從事件的 broadcastOn
方法傳回 PresenceChannel
的實例。
/** * Get the channels the event should broadcast on. * * @return array<int, \Illuminate\Broadcasting\Channel> */public function broadcastOn(): array{ return [ new PresenceChannel('chat.'.$this->message->room_id), ];}
與其他事件一樣,您可以使用 broadcast
輔助函式和 toOthers
方法來排除當前使用者接收廣播。
broadcast(new NewMessage($message)); broadcast(new NewMessage($message))->toOthers();
與其他類型的事件一樣,您可以使用 Echo 的 listen
方法來監聽傳送到出席通道的事件。
Echo.join(`chat.${roomId}`) .here(/* ... */) .joining(/* ... */) .leaving(/* ... */) .listen('NewMessage', (e) => { // ... });
模型廣播
在閱讀以下關於模型廣播的文件之前,我們建議您熟悉 Laravel 模型廣播服務的一般概念,以及如何手動建立和監聽廣播事件。
當應用程式的 Eloquent 模型建立、更新或刪除時,廣播事件是很常見的。當然,這可以透過手動為 Eloquent 模型狀態變更定義自訂事件並使用 ShouldBroadcast
介面標記這些事件來輕鬆完成。
但是,如果您在應用程式中沒有將這些事件用於任何其他目的,則僅為了廣播它們而建立事件類別可能會很麻煩。為了彌補這一點,Laravel 允許您指示 Eloquent 模型應自動廣播其狀態變更。
若要開始,您的 Eloquent 模型應該使用 Illuminate\Database\Eloquent\BroadcastsEvents
特徵。此外,模型應該定義一個 broadcastOn
方法,該方法會傳回模型事件應該廣播到的通道陣列。
<?php namespace App\Models; use Illuminate\Broadcasting\Channel;use Illuminate\Broadcasting\PrivateChannel;use Illuminate\Database\Eloquent\BroadcastsEvents;use Illuminate\Database\Eloquent\Factories\HasFactory;use Illuminate\Database\Eloquent\Model;use Illuminate\Database\Eloquent\Relations\BelongsTo; class Post extends Model{ use BroadcastsEvents, HasFactory; /** * Get the user that the post belongs to. */ public function user(): BelongsTo { return $this->belongsTo(User::class); } /** * Get the channels that model events should broadcast on. * * @return array<int, \Illuminate\Broadcasting\Channel|\Illuminate\Database\Eloquent\Model> */ public function broadcastOn(string $event): array { return [$this, $this->user]; }}
一旦您的模型包含此特徵並定義其廣播通道,它就會開始在建立、更新、刪除、軟刪除或還原模型實例時自動廣播事件。
此外,您可能已經注意到 broadcastOn
方法接收到字串 $event
引數。此引數包含模型上發生的事件類型,其值將為 created
、updated
、deleted
、trashed
或 restored
。透過檢查此變數的值,您可以判斷模型是否應針對特定事件廣播到哪些通道(如果有的話)。
/** * Get the channels that model events should broadcast on. * * @return array<string, array<int, \Illuminate\Broadcasting\Channel|\Illuminate\Database\Eloquent\Model>> */public function broadcastOn(string $event): array{ return match ($event) { 'deleted' => [], default => [$this, $this->user], };}
自訂模型廣播事件建立
有時候,您可能會想要自訂 Laravel 如何建立底層的廣播模型事件。您可以在您的 Eloquent 模型上定義一個 newBroadcastableEvent
方法來達成此目的。這個方法應該回傳一個 Illuminate\Database\Eloquent\BroadcastableModelEventOccurred
實例。
use Illuminate\Database\Eloquent\BroadcastableModelEventOccurred; /** * Create a new broadcastable model event for the model. */protected function newBroadcastableEvent(string $event): BroadcastableModelEventOccurred{ return (new BroadcastableModelEventOccurred( $this, $event ))->dontBroadcastToCurrentUser();}
模型廣播慣例
頻道慣例
您可能已經注意到,在上面的模型範例中,broadcastOn
方法並未回傳 Channel
實例。取而代之的是,直接回傳 Eloquent 模型。如果您的模型的 broadcastOn
方法回傳了一個 Eloquent 模型實例(或包含在方法回傳的陣列中),Laravel 將會自動使用模型的類別名稱和主鍵識別符,為該模型實例化一個私有頻道實例作為頻道名稱。
因此,一個 App\Models\User
模型,其 id
為 1
,將會被轉換為一個名稱為 App.Models.User.1
的 Illuminate\Broadcasting\PrivateChannel
實例。當然,除了從您模型的 broadcastOn
方法回傳 Eloquent 模型實例之外,您也可以回傳完整的 Channel
實例,以便完全控制模型的頻道名稱。
use Illuminate\Broadcasting\PrivateChannel; /** * Get the channels that model events should broadcast on. * * @return array<int, \Illuminate\Broadcasting\Channel> */public function broadcastOn(string $event): array{ return [ new PrivateChannel('user.'.$this->id) ];}
如果您計畫從您模型的 broadcastOn
方法明確回傳一個頻道實例,您可以將 Eloquent 模型實例傳遞給頻道的建構子。當這樣做時,Laravel 將會使用上面討論的模型頻道慣例,將 Eloquent 模型轉換為頻道名稱字串。
return [new Channel($this->user)];
如果您需要判斷模型的頻道名稱,您可以呼叫任何模型實例上的 broadcastChannel
方法。例如,對於一個 id
為 1
的 App\Models\User
模型,此方法會回傳字串 App.Models.User.1
。
$user->broadcastChannel()
事件慣例
由於模型廣播事件與您應用程式 App\Events
目錄中的「實際」事件無關,因此會根據慣例指派名稱和酬載。Laravel 的慣例是使用模型類別名稱(不包含命名空間)和觸發廣播的模型事件名稱來廣播事件。
因此,例如,對 App\Models\Post
模型的更新會以 PostUpdated
為名廣播事件到您的用戶端應用程式,並帶有以下酬載。
{ "model": { "id": 1, "title": "My first post" ... }, ... "socket": "someSocketId",}
刪除 App\Models\User
模型將會廣播一個名為 UserDeleted
的事件。
如果您願意,您可以藉由在模型中加入 broadcastAs
和 broadcastWith
方法來定義自訂的廣播名稱和酬載。這些方法會接收正在發生的模型事件/操作的名稱,讓您可以針對每個模型操作自訂事件的名稱和酬載。如果 broadcastAs
方法回傳 null
,Laravel 將會在廣播事件時使用上面討論的模型廣播事件名稱慣例。
/** * The model event's broadcast name. */public function broadcastAs(string $event): string|null{ return match ($event) { 'created' => 'post.created', default => null, };} /** * Get the data to broadcast for the model. * * @return array<string, mixed> */public function broadcastWith(string $event): array{ return match ($event) { 'created' => ['title' => $this->title], default => ['model' => $this], };}
監聽模型廣播
一旦您將 BroadcastsEvents
Trait 加入您的模型,並定義您模型的 broadcastOn
方法後,您就準備好開始在您的用戶端應用程式中監聽廣播的模型事件。在開始之前,您可能需要參考關於監聽事件的完整文件。
首先,使用 private
方法來取得一個頻道實例,然後呼叫 listen
方法來監聽指定的事件。通常,提供給 private
方法的頻道名稱應該與 Laravel 的模型廣播慣例相符。
一旦您取得頻道實例後,您可以使用 listen
方法來監聽特定的事件。由於模型廣播事件與您應用程式 App\Events
目錄中的「實際」事件無關,因此事件名稱必須以 .
開頭,以表明它不屬於特定的命名空間。每個模型廣播事件都有一個 model
屬性,其中包含模型的所有可廣播屬性。
Echo.private(`App.Models.User.${this.user.id}`) .listen('.PostUpdated', (e) => { console.log(e.model); });
客戶端事件
當使用 Pusher Channels 時,您必須在您的 應用程式儀表板的「應用程式設定」區段中啟用「客戶端事件」選項,才能傳送客戶端事件。
有時您可能希望將事件廣播給其他已連線的客戶端,而無需觸及您的 Laravel 應用程式。這對於諸如「正在輸入」通知之類的事情特別有用,您想要提醒您的應用程式使用者,另一個使用者正在特定畫面上輸入訊息。
要廣播客戶端事件,您可以使用 Echo 的 whisper
方法。
Echo.private(`chat.${roomId}`) .whisper('typing', { name: this.user.name });
要監聽客戶端事件,您可以使用 listenForWhisper
方法。
Echo.private(`chat.${roomId}`) .listenForWhisper('typing', (e) => { console.log(e.name); });
通知
藉由將事件廣播與通知配對,您的 JavaScript 應用程式可以在通知發生時接收新的通知,而無需重新整理頁面。在開始之前,請務必閱讀關於使用廣播通知頻道的文件。
一旦您設定好通知以使用廣播頻道,您就可以使用 Echo 的 notification
方法來監聽廣播事件。請記住,頻道名稱應該與接收通知的實體類別名稱相符。
Echo.private(`App.Models.User.${userId}`) .notification((notification) => { console.log(notification.type); });
在這個範例中,所有透過 broadcast
頻道傳送給 App\Models\User
實例的通知都將被回呼接收。您應用程式的 routes/channels.php
檔案中包含一個用於 App.Models.User.{id}
頻道的頻道授權回呼。