廣播
簡介
在許多現代 Web 應用中,WebSocket 被用於實現即時、動態更新的使用者介面。當伺服器端資料更新時,通常會透過 WebSocket 連線傳送一條訊息,由客戶端進行處理。與不斷輪詢伺服器以獲取 UI 所需的更新資料相比,WebSocket 提供了一種更高效的選擇。
例如,假設你的應用程式能夠將使用者資料匯出為 CSV 檔案併發送到其郵箱。由於建立 CSV 檔案需要幾分鐘時間,你選擇在一個 佇列任務 中生成併發送郵件。當 CSV 建立併發送完畢後,我們可以使用事件廣播分發一個 App\Events\UserDataExported 事件,該事件會被應用程式的 JavaScript 端接收。收到事件後,我們可以向用戶顯示一條提示資訊,告知 CSV 已傳送至其郵箱,而無需重新整理頁面。
為了協助你構建此類功能,Laravel 使得透過 WebSocket 連線“廣播”你的服務端 Laravel 事件 變得非常簡單。廣播 Laravel 事件允許你在服務端 Laravel 應用和客戶端 JavaScript 應用之間共享相同的事件名稱和資料。
廣播的核心概念很簡單:客戶端在前端訂閱命名頻道,而你的 Laravel 後端則向這些頻道廣播事件。這些事件可以包含任何你希望在前端獲取的額外資料。
支援的驅動
預設情況下,Laravel 包含三種服務端廣播驅動供選擇:Laravel Reverb、Pusher Channels 和 Ably。
在深入瞭解事件廣播之前,請確保你已經閱讀了 Laravel 關於 事件和監聽器 的文件。
快速入門
預設情況下,新建立的 Laravel 應用並未啟用廣播功能。你可以使用 install:broadcasting Artisan 命令來啟用廣播:
1php artisan install:broadcasting
install:broadcasting 命令會詢問你希望使用哪種事件廣播服務。此外,它還會建立 config/broadcasting.php 配置檔案和 routes/channels.php 檔案,你可以在這些檔案中註冊應用的廣播授權路由和回撥。
Laravel 開箱即用地支援多種廣播驅動:Laravel Reverb、Pusher Channels、Ably,以及用於本地開發和除錯的 log 驅動。此外,還包含一個 null 驅動,用於在測試時停用廣播。config/broadcasting.php 配置檔案中包含了每種驅動的配置示例。
應用程式所有的事件廣播配置都儲存在 config/broadcasting.php 配置檔案中。如果你的應用中不存在此檔案,請放心,執行 install:broadcasting Artisan 命令時它會自動建立。
後續步驟
啟用事件廣播後,你就可以進一步學習 定義廣播事件 和 監聽事件 了。如果你正在使用 Laravel 的 React 或 Vue 入門套件,你可以使用 Echo 的 useEcho hook 來監聽事件。
在廣播任何事件之前,你應該首先配置並執行一個 佇列工作器。所有的事件廣播都是透過佇列任務完成的,以確保應用的響應時間不會因事件廣播而受到嚴重影響。
服務端安裝
要開始使用 Laravel 的事件廣播,我們需要在 Laravel 應用中進行一些配置,並安裝一些包。
事件廣播由服務端廣播驅動實現,它將你的 Laravel 事件進行廣播,以便 Laravel Echo(一個 JavaScript 庫)能夠在瀏覽器客戶端接收到。別擔心,我們將逐步介紹安裝過程的每個部分。
Reverb
若要在使用 Reverb 作為事件廣播器時快速啟用對 Laravel 廣播功能的支援,請使用 --reverb 選項呼叫 install:broadcasting Artisan 命令。該 Artisan 命令將安裝 Reverb 所需的 Composer 和 NPM 包,並使用相應的變數更新應用的 .env 檔案。
1php artisan install:broadcasting --reverb
手動安裝
執行 install:broadcasting 命令時,系統會提示你安裝 Laravel Reverb。當然,你也可以使用 Composer 包管理器手動安裝 Reverb:
1composer require laravel/reverb
安裝包後,你可以執行 Reverb 的安裝命令來發布配置檔案、新增 Reverb 所需的環境變數,並啟用應用中的事件廣播:
1php artisan reverb:install
你可以在 Reverb 文件 中找到詳細的 Reverb 安裝和使用說明。
Pusher Channels
若要在使用 Pusher 作為事件廣播器時快速啟用對 Laravel 廣播功能的支援,請使用 --pusher 選項呼叫 install:broadcasting Artisan 命令。該命令將詢問你的 Pusher 憑據,安裝 Pusher PHP 和 JavaScript SDK,並使用相應的變數更新應用的 .env 檔案。
1php artisan install:broadcasting --pusher
手動安裝
若要手動安裝 Pusher 支援,你應該使用 Composer 包管理器安裝 Pusher Channels PHP SDK:
1composer require pusher/pusher-php-server
接下來,你應該在 config/broadcasting.php 配置檔案中配置你的 Pusher Channels 憑據。該檔案中已包含一個 Pusher Channels 配置示例,允許你快速指定金鑰、秘鑰和應用 ID。通常,你應該在應用的 .env 檔案中配置這些憑據。
1PUSHER_APP_ID="your-pusher-app-id"2PUSHER_APP_KEY="your-pusher-key"3PUSHER_APP_SECRET="your-pusher-secret"4PUSHER_HOST=5PUSHER_PORT=4436PUSHER_SCHEME="https"7PUSHER_APP_CLUSTER="mt1"
config/broadcasting.php 檔案中的 pusher 配置還允許你指定 Channels 支援的其他 options,例如 cluster(叢集)。
然後,在應用的 .env 檔案中將 BROADCAST_CONNECTION 環境變數設定為 pusher:
1BROADCAST_CONNECTION=pusher
最後,你可以安裝並配置 Laravel Echo 了,它將負責在客戶端接收廣播事件。
Ably
以下文件討論瞭如何以“Pusher 相容”模式使用 Ably。但是,Ably 團隊建議並維護著一個能夠充分利用 Ably 獨特功能的廣播器和 Echo 客戶端。關於使用 Ably 維護的驅動程式的更多資訊,請 查閱 Ably 的 Laravel 廣播器文件。
若要在使用 Ably 作為事件廣播器時快速啟用對 Laravel 廣播功能的支援,請使用 --ably 選項呼叫 install:broadcasting Artisan 命令。該命令將詢問你的 Ably 憑據,安裝 Ably PHP 和 JavaScript SDK,並使用相應的變數更新應用的 .env 檔案。
1php artisan install:broadcasting --ably
在繼續之前,你應該在 Ably 應用設定中啟用 Pusher 協議支援。你可以在 Ably 應用設定面板的“Protocol Adapter Settings”(協議介面卡設定)部分啟用此功能。
手動安裝
若要手動安裝 Ably 支援,你應該使用 Composer 包管理器安裝 Ably PHP SDK:
1composer require ably/ably-php
接下來,你應該在 config/broadcasting.php 配置檔案中配置你的 Ably 憑據。該檔案中已包含一個 Ably 配置示例,允許你快速指定 key。通常,此值應透過 ABLY_KEY 環境變數 進行設定。
1ABLY_KEY=your-ably-key
然後,在應用的 .env 檔案中將 BROADCAST_CONNECTION 環境變數設定為 ably:
1BROADCAST_CONNECTION=ably
最後,你可以安裝並配置 Laravel Echo 了,它將負責在客戶端接收廣播事件。
客戶端安裝
Reverb
Laravel Echo 是一個 JavaScript 庫,它能讓你輕鬆訂閱頻道並監聽服務端廣播驅動所廣播的事件。
當透過 install:broadcasting Artisan 命令安裝 Laravel Reverb 時,Reverb 和 Echo 的腳手架及配置將自動注入到你的應用中。然而,如果你希望手動配置 Laravel Echo,可以按照以下說明進行操作。
手動安裝
若要為應用的前端手動配置 Laravel Echo,首先安裝 pusher-js 包,因為 Reverb 使用 Pusher 協議進行 WebSocket 訂閱、頻道和訊息傳輸:
1npm install --save-dev laravel-echo pusher-js
Echo 安裝完成後,你就可以在應用的 JavaScript 中建立一個新的 Echo 例項了。一個很好的做法是在 Laravel 框架自帶的 resources/js/bootstrap.js 檔案底部進行配置:
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 ?? 80,11 wssPort: import.meta.env.VITE_REVERB_PORT ?? 443,12 forceTLS: (import.meta.env.VITE_REVERB_SCHEME ?? 'https') === 'https',13 enabledTransports: ['ws', 'wss'],14});
1import { configureEcho } from "@laravel/echo-react"; 2 3configureEcho({ 4 broadcaster: "reverb", 5 // key: import.meta.env.VITE_REVERB_APP_KEY, 6 // wsHost: import.meta.env.VITE_REVERB_HOST, 7 // wsPort: import.meta.env.VITE_REVERB_PORT, 8 // wssPort: import.meta.env.VITE_REVERB_PORT, 9 // forceTLS: (import.meta.env.VITE_REVERB_SCHEME ?? 'https') === 'https',10 // enabledTransports: ['ws', 'wss'],11});
1import { configureEcho } from "@laravel/echo-vue"; 2 3configureEcho({ 4 broadcaster: "reverb", 5 // key: import.meta.env.VITE_REVERB_APP_KEY, 6 // wsHost: import.meta.env.VITE_REVERB_HOST, 7 // wsPort: import.meta.env.VITE_REVERB_PORT, 8 // wssPort: import.meta.env.VITE_REVERB_PORT, 9 // forceTLS: (import.meta.env.VITE_REVERB_SCHEME ?? 'https') === 'https',10 // enabledTransports: ['ws', 'wss'],11});
接下來,你應該編譯應用的資源:
1npm 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 訂閱、頻道和訊息傳輸:
1npm install --save-dev laravel-echo pusher-js
Echo 安裝完成後,你就可以在應用的 resources/js/bootstrap.js 檔案中建立一個新的 Echo 例項了:
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: true11});
1import { configureEcho } from "@laravel/echo-react"; 2 3configureEcho({ 4 broadcaster: "pusher", 5 // key: import.meta.env.VITE_PUSHER_APP_KEY, 6 // cluster: import.meta.env.VITE_PUSHER_APP_CLUSTER, 7 // forceTLS: true, 8 // wsHost: import.meta.env.VITE_PUSHER_HOST, 9 // wsPort: import.meta.env.VITE_PUSHER_PORT,10 // wssPort: import.meta.env.VITE_PUSHER_PORT,11 // enabledTransports: ["ws", "wss"],12});
1import { configureEcho } from "@laravel/echo-vue"; 2 3configureEcho({ 4 broadcaster: "pusher", 5 // key: import.meta.env.VITE_PUSHER_APP_KEY, 6 // cluster: import.meta.env.VITE_PUSHER_APP_CLUSTER, 7 // forceTLS: true, 8 // wsHost: import.meta.env.VITE_PUSHER_HOST, 9 // wsPort: import.meta.env.VITE_PUSHER_PORT,10 // wssPort: import.meta.env.VITE_PUSHER_PORT,11 // enabledTransports: ["ws", "wss"],12});
接下來,你應該在應用的 .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: import.meta.env.VITE_PUSHER_APP_KEY 7} 8 9window.Echo = new Echo({10 ...options,11 client: new Pusher(options.key, options)12});
Ably
以下文件討論瞭如何以“Pusher 相容”模式使用 Ably。但是,Ably 團隊建議並維護著一個能夠充分利用 Ably 獨特功能的廣播器和 Echo 客戶端。關於使用 Ably 維護的驅動程式的更多資訊,請 查閱 Ably 的 Laravel 廣播器文件。
Laravel Echo 是一個 JavaScript 庫,它能讓你輕鬆訂閱頻道並監聽服務端廣播驅動所廣播的事件。
當透過 install:broadcasting --ably Artisan 命令安裝廣播支援時,Ably 和 Echo 的腳手架及配置將自動注入到你的應用中。如果你希望手動配置 Laravel Echo,可以按照以下說明進行操作。
手動安裝
若要為應用的前端手動配置 Laravel Echo,首先安裝 laravel-echo 和 pusher-js 包,它們使用 Pusher 協議進行 WebSocket 訂閱、頻道和訊息傳輸:
1npm install --save-dev laravel-echo pusher-js
在繼續之前,你應該在 Ably 應用設定中啟用 Pusher 協議支援。你可以在 Ably 應用設定面板的“Protocol Adapter Settings”(協議介面卡設定)部分啟用此功能。
Echo 安裝完成後,你就可以在應用的 resources/js/bootstrap.js 檔案中建立一個新的 Echo 例項了:
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});
1import { configureEcho } from "@laravel/echo-react"; 2 3configureEcho({ 4 broadcaster: "ably", 5 // key: import.meta.env.VITE_ABLY_PUBLIC_KEY, 6 // wsHost: "realtime-pusher.ably.io", 7 // wsPort: 443, 8 // disableStats: true, 9 // encrypted: true,10});
1import { configureEcho } from "@laravel/echo-vue"; 2 3configureEcho({ 4 broadcaster: "ably", 5 // key: import.meta.env.VITE_ABLY_PUBLIC_KEY, 6 // wsHost: "realtime-pusher.ably.io", 7 // wsPort: 443, 8 // disableStats: true, 9 // encrypted: true,10});
你可能已經注意到,我們的 Ably Echo 配置引用了一個 VITE_ABLY_PUBLIC_KEY 環境變數。該變數的值應該是你的 Ably 公鑰。公鑰是你 Ably 金鑰中 : 字元之前的那部分。
根據需求調整 Echo 配置後,你就可以編譯應用的資源了:
1npm run dev
若要了解更多關於編譯應用 JavaScript 資源的資訊,請查閱 Vite 文件。
概念概覽
Laravel 的事件廣播允許你使用基於驅動的 WebSocket 方法,將服務端 Laravel 事件廣播到客戶端 JavaScript 應用。目前,Laravel 自帶 Laravel Reverb、Pusher Channels 和 Ably 驅動。可以使用 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 ShouldBroadcast13{14 /**15 * The order instance.16 *17 * @var \App\Models\Order18 */19 public $order;20}
ShouldBroadcast 介面要求我們的事件定義一個 broadcastOn 方法。該方法負責返回事件應廣播到的頻道。生成的事件類中已經定義了該方法的空存根,我們只需要填充其細節。我們只希望訂單的建立者能夠檢視狀態更新,因此我們將在一個與訂單繫結的私有頻道上進行廣播:
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 方法接收兩個引數:頻道名稱和一個返回 true 或 false 的回撥,用於指示使用者是否被授權監聽該頻道。
所有的授權回撥都會接收當前已認證的使用者作為第一個引數,以及任何額外的萬用字元引數作為後續引數。在這個例子中,我們使用 {orderId} 佔位符來表示頻道名稱中的“ID”部分是一個萬用字元。
監聽事件廣播
接下來,剩下的工作就是在 JavaScript 應用中監聽該事件。我們可以使用 Laravel Echo。Laravel Echo 內建的 React 和 Vue Hooks 使得上手非常簡單,預設情況下,事件的所有公共屬性都將包含在廣播事件中:
1import { useEcho } from "@laravel/echo-react";2 3useEcho(4 `orders.${orderId}`,5 "OrderShipmentStatusUpdated",6 (e) => {7 console.log(e.order);8 },9);
1<script setup lang="ts"> 2import { useEcho } from "@laravel/echo-vue"; 3 4useEcho( 5 `orders.${orderId}`, 6 "OrderShipmentStatusUpdated", 7 (e) => { 8 console.log(e.order); 9 },10);11</script>
定義廣播事件
要通知 Laravel 某個事件應該被廣播,你必須在事件類中實現 Illuminate\Contracts\Broadcasting\ShouldBroadcast 介面。框架生成的所有事件類都已經匯入了這個介面,因此你可以輕鬆地將其新增到任何事件中。
ShouldBroadcast 介面要求你實現一個單一方法:broadcastOn。該方法應該返回一個頻道或頻道陣列。頻道應該是 Channel、PrivateChannel 或 PresenceChannel 的例項。Channel 例項代表任何使用者都可以訂閱的公共頻道,而 PrivateChannels 和 PresenceChannels 則代表需要 頻道授權 的私有頻道。
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 ShouldBroadcast14{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(): array30 {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(): string5{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(): array7{8 return ['id' => $this->user->id];9}
廣播佇列
預設情況下,每個廣播事件都會被放置在 queue.php 配置檔案中指定的預設佇列連線的預設佇列中。你可以使用事件類上的 Connection 和 Queue 屬性來自定義廣播器使用的佇列連線和名稱:
1use Illuminate\Queue\Attributes\Connection;2use Illuminate\Queue\Attributes\Queue;3 4#[Connection('redis')]5#[Queue('default')]6class ServerCreated implements ShouldBroadcast7{8 // ...9}
或者,你可以透過在事件中定義 broadcastQueue 方法來自定義佇列名稱:
1/**2 * The name of the queue on which to place the broadcasting job.3 */4public function broadcastQueue(): string5{6 return 'default';7}
如果你希望使用 sync 佇列而不是預設的佇列驅動來廣播事件,可以實現 ShouldBroadcastNow 介面而不是 ShouldBroadcast。
1<?php 2 3namespace App\Events; 4 5use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow; 6 7class OrderShipmentStatusUpdated implements ShouldBroadcastNow 8{ 9 // ...10}
廣播條件
有時你只想在滿足特定條件時才廣播事件。你可以透過在事件類中新增 broadcastWhen 方法來定義這些條件:
1/**2 * Determine if this event should broadcast.3 */4public function broadcastWhen(): bool5{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, ShouldDispatchAfterCommit10{11 use SerializesModels;12}
要了解更多關於解決這些問題的資訊,請檢視關於排隊作業與資料庫事務的文件。
頻道授權
私有頻道要求你授權當前已認證的使用者確實可以監聽該頻道。這是透過向 Laravel 應用發起一個帶有頻道名稱的 HTTP 請求來實現的,並由應用確定使用者是否可以監聽該頻道。使用 Laravel Echo 時,授權訂閱私有頻道的 HTTP 請求會自動發起。
當安裝廣播功能時,Laravel 會嘗試自動註冊 /broadcasting/auth 路由來處理授權請求。如果 Laravel 未能自動註冊這些路由,你可以在應用的 /bootstrap/app.php 檔案中手動註冊它們:
1->withRouting(2 web: __DIR__.'/../routes/web.php',3 channels: __DIR__.'/../routes/channels.php',4 health: '/up',5)
定義授權回撥
接下來,我們需要定義邏輯來確定當前已認證的使用者是否可以監聽某個給定頻道。這在 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 方法接收兩個引數:頻道名稱和一個返回 true 或 false 的回撥,用於指示使用者是否被授權監聽該頻道。
所有的授權回撥都會接收當前已認證的使用者作為第一個引數,以及任何額外的萬用字元引數作為後續引數。在這個例子中,我們使用 {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 廣播頻道透過應用的預設身份驗證守衛(guard)驗證當前使用者。如果使用者未認證,頻道授權將自動被拒絕,且授權回撥永遠不會執行。然而,如有必要,你可以指定多個自定義守衛來驗證傳入請求:
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|bool19 {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 例項時,會為該連線分配一個套接字 ID(socket ID)。如果你正在使用全域性的 Axios 例項從 JavaScript 應用發起 HTTP 請求,套接字 ID 將自動作為 X-Socket-ID 頭資訊附加到每個傳出請求中。然後,當你呼叫 toOthers 方法時,Laravel 會從頭資訊中提取套接字 ID,並指示廣播器不要將訊息廣播給任何具有該套接字 ID 的連線。
如果你沒有使用全域性 Axios 例項,則需要手動配置 JavaScript 應用,在所有傳出請求中傳送 X-Socket-ID 頭資訊。你可以使用 Echo.socketId 方法檢索該套接字 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 ShouldBroadcast14{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}
使用 as 和 with 方法,你可以自定義事件的名稱和資料:
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 頻道上廣播匿名事件,可以使用 private 和 presence 方法:
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 在廣播事件時遇到錯誤,通常會丟擲一個異常,導致終端使用者看到應用錯誤。由於事件廣播通常是對應用核心功能的補充,你可以透過在事件上實現 ShouldRescue 介面,來防止這些異常打斷使用者體驗。
實現 ShouldRescue 介面的事件會在廣播嘗試期間自動利用 Laravel 的 rescue 助手函式。該助手函式會捕獲任何異常,將其報告給應用的異常處理器進行日誌記錄,並允許應用繼續正常執行,而不會打斷使用者的操作流程。
1<?php 2 3namespace App\Events; 4 5use Illuminate\Contracts\Broadcasting\ShouldBroadcast; 6use Illuminate\Contracts\Broadcasting\ShouldRescue; 7 8class ServerCreated implements ShouldBroadcast, ShouldRescue 9{10 // ...11}
接收廣播
監聽事件
一旦你 安裝並例項化了 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 });
使用 React 或 Vue
Laravel Echo 包含 React 和 Vue Hooks,使得監聽事件變得輕而易舉。要開始使用,呼叫 useEcho hook,它用於監聽私有事件。當所消費的元件解除安裝時,useEcho hook 會自動離開頻道:
1import { useEcho } from "@laravel/echo-react";2 3useEcho(4 `orders.${orderId}`,5 "OrderShipmentStatusUpdated",6 (e) => {7 console.log(e.order);8 },9);
1<script setup lang="ts"> 2import { useEcho } from "@laravel/echo-vue"; 3 4useEcho( 5 `orders.${orderId}`, 6 "OrderShipmentStatusUpdated", 7 (e) => { 8 console.log(e.order); 9 },10);11</script>
你可以透過提供一個事件陣列給 useEcho 來監聽多個事件:
1useEcho(2 `orders.${orderId}`,3 ["OrderShipmentStatusUpdated", "OrderShipped"],4 (e) => {5 console.log(e.order);6 },7);
你還可以指定廣播事件載荷資料的形狀(shape),從而提供更好的型別安全性和編輯便利性:
1type OrderData = { 2 order: { 3 id: number; 4 user: { 5 id: number; 6 name: string; 7 }; 8 created_at: string; 9 };10};11 12useEcho<OrderData>(`orders.${orderId}`, "OrderShipmentStatusUpdated", (e) => {13 console.log(e.order.id);14 console.log(e.order.user.id);15});
當所消費的元件解除安裝時,useEcho hook 會自動離開頻道;不過,你也可以利用返回的函式在必要時以程式設計方式手動停止/啟動頻道的監聽:
1import { useEcho } from "@laravel/echo-react"; 2 3const { leaveChannel, leave, stopListening, listen } = useEcho( 4 `orders.${orderId}`, 5 "OrderShipmentStatusUpdated", 6 (e) => { 7 console.log(e.order); 8 }, 9);10 11// Stop listening without leaving channel...12stopListening();13 14// Start listening again...15listen();16 17// Leave channel...18leaveChannel();19 20// Leave a channel and also its associated private and presence channels...21leave();
1<script setup lang="ts"> 2import { useEcho } from "@laravel/echo-vue"; 3 4const { leaveChannel, leave, stopListening, listen } = useEcho( 5 `orders.${orderId}`, 6 "OrderShipmentStatusUpdated", 7 (e) => { 8 console.log(e.order); 9 },10);11 12// Stop listening without leaving channel...13stopListening();14 15// Start listening again...16listen();17 18// Leave channel...19leaveChannel();20 21// Leave a channel and also its associated private and presence channels...22leave();23</script>
連線到公共頻道
要連線到公共頻道,可以使用 useEchoPublic hook:
1import { useEchoPublic } from "@laravel/echo-react";2 3useEchoPublic("posts", "PostPublished", (e) => {4 console.log(e.post);5});
1<script setup lang="ts">2import { useEchoPublic } from "@laravel/echo-vue";3 4useEchoPublic("posts", "PostPublished", (e) => {5 console.log(e.post);6});7</script>
連線到 Presence 頻道
要連線到 Presence 頻道,可以使用 useEchoPresence hook:
1import { useEchoPresence } from "@laravel/echo-react";2 3useEchoPresence("posts", "PostPublished", (e) => {4 console.log(e.post);5});
1<script setup lang="ts">2import { useEchoPresence } from "@laravel/echo-vue";3 4useEchoPresence("posts", "PostPublished", (e) => {5 console.log(e.post);6});7</script>
連線狀態
你可以使用 useConnectionStatus hook 獲取當前 WebSocket 連線狀態,它提供自動隨連線狀態變化而更新的響應式狀態:
1import { useConnectionStatus } from "@laravel/echo-react";2 3function ConnectionIndicator() {4 const status = useConnectionStatus();5 6 return <div>Connection: {status}</div>;7}
1<script setup lang="ts">2import { useConnectionStatus } from "@laravel/echo-vue";3 4const status = useConnectionStatus();5</script>6 7<template>8 <div>Connection: {{ status }}</div>9</template>
可能的取值有:
connected- 已成功連線到 WebSocket 伺服器。connecting- 正在進行初始連線嘗試。reconnecting- 斷開連線後正在嘗試重新連線。disconnected- 未連線且沒有嘗試重新連線。failed- 連線失敗且不會再重試。
Presence 頻道(線上狀態頻道)
Presence 頻道建立在私有頻道的安全性之上,同時額外提供了感知頻道內訂閱者的功能。這使得構建強大的協作應用功能變得簡單,例如當其他使用者正在檢視相同頁面時通知使用者,或者列出聊天室中的線上使用者。
授權 Presence 頻道
所有 Presence 頻道也是私有頻道;因此,使用者必須被 授權訪問。然而,在為 Presence 頻道定義授權回撥時,如果使用者被授權加入頻道,你不會返回 true,而是應該返回一個包含使用者資料的陣列。
授權回撥返回的資料將在 JavaScript 應用的 Presence 頻道事件監聽器中可用。如果使用者未被授權加入 Presence 頻道,你應該返回 false 或 null。
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 頻道
要加入 Presence 頻道,可以使用 Echo 的 join 方法。join 方法將返回一個 PresenceChannel 實現,它除了公開 listen 方法外,還允許你訂閱 here、joining 和 leaving 事件:
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 方法會在使用者離開頻道時執行。當認證端點返回 200 以外的 HTTP 狀態碼或解析返回的 JSON 出現問題時,會執行 error 方法。
向 Presence 頻道廣播
Presence 頻道可以像公共或私有頻道一樣接收事件。以聊天室為例,我們可能想向聊天室的 Presence 頻道廣播 NewMessage 事件。為此,我們將從事件的 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 Model13{14 use BroadcastsEvents, HasFactory;15 16 /**17 * Get the user that the post belongs to.18 */19 public function user(): BelongsTo20 {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): array30 {31 return [$this, $this->user];32 }33}
一旦模型包含此 trait 並定義了廣播頻道,它就會在模型例項被建立、更新、刪除、軟刪除(trashed)或恢復時自動廣播事件。
此外,你可能注意到 broadcastOn 方法接收一個字串引數 $event。該引數包含模型上發生的事件型別,值為 created、updated、deleted、trashed 或 restored。透過檢查該變數的值,你可以確定模型應針對特定事件廣播到哪些頻道(如果有的話):
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, $event10 ))->dontBroadcastToCurrentUser();11}
模型廣播約定
頻道約定
正如你可能注意到的,上面模型示例中的 broadcastOn 方法沒有返回 Channel 例項,而是直接返回了 Eloquent 模型。如果模型的 broadcastOn 方法返回了 Eloquent 模型例項(或包含在方法返回的陣列中),Laravel 將使用模型類名和主鍵識別符號作為頻道名稱,自動為該模型例項化一個私有頻道例項。
因此,一個 id 為 1 的 App\Models\User 模型將被轉換為一個名為 App.Models.User.1 的 Illuminate\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 方法。例如,對於 id 為 1 的 App\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 的事件。
如果你願意,可以透過在模型中新增 broadcastAs 和 broadcastWith 方法來定義自定義的廣播名稱和載荷。這些方法接收正在發生的模型事件/操作名稱,允許你為每個模型操作自定義事件名稱和載荷。如果 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): array18{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('.UserUpdated', (e) => {3 console.log(e.model);4 });
使用 React 或 Vue
如果你正在使用 React 或 Vue,可以使用 Laravel Echo 附帶的 useEchoModel hook 來輕鬆監聽模型廣播:
1import { useEchoModel } from "@laravel/echo-react";2 3useEchoModel("App.Models.User", userId, ["UserUpdated"], (e) => {4 console.log(e.model);5});
1<script setup lang="ts">2import { useEchoModel } from "@laravel/echo-vue";3 4useEchoModel("App.Models.User", userId, ["UserUpdated"], (e) => {5 console.log(e.model);6});7</script>
你還可以指定模型事件載荷資料的形狀,從而提供更好的型別安全性和編輯便利性:
1type User = { 2 id: number; 3 name: string; 4 email: string; 5}; 6 7useEchoModel<User, "App.Models.User">("App.Models.User", userId, ["UserUpdated"], (e) => { 8 console.log(e.model.id); 9 console.log(e.model.name);10});
客戶端事件
當使用 Pusher Channels 時,你必須在 應用儀表板 的“App Settings”部分啟用“Client Events”選項,以便傳送客戶端事件。
有時你可能希望向其他已連線的客戶端廣播事件,而無需觸及 Laravel 應用本身。這對於諸如“輸入中”(typing)通知之類的事情特別有用,你希望在特定螢幕上提醒應用的其他使用者某人正在輸入訊息。
若要廣播客戶端事件,可以使用 Echo 的 whisper 方法:
1Echo.private(`chat.${roomId}`)2 .whisper('typing', {3 name: this.user.name4 });
1import { useEcho } from "@laravel/echo-react";2 3const { channel } = useEcho(`chat.${roomId}`, ['update'], (e) => {4 console.log('Chat event received:', e);5});6 7channel().whisper('typing', { name: user.name });
1<script setup lang="ts">2import { useEcho } from "@laravel/echo-vue";3 4const { channel } = useEcho(`chat.${roomId}`, ['update'], (e) => {5 console.log('Chat event received:', e);6});7 8channel().whisper('typing', { name: user.name });9</script>
要監聽客戶端事件,可以使用 listenForWhisper 方法:
1Echo.private(`chat.${roomId}`)2 .listenForWhisper('typing', (e) => {3 console.log(e.name);4 });
1import { useEcho } from "@laravel/echo-react";2 3const { channel } = useEcho(`chat.${roomId}`, ['update'], (e) => {4 console.log('Chat event received:', e);5});6 7channel().listenForWhisper('typing', (e) => {8 console.log(e.name);9});
1<script setup lang="ts"> 2import { useEcho } from "@laravel/echo-vue"; 3 4const { channel } = useEcho(`chat.${roomId}`, ['update'], (e) => { 5 console.log('Chat event received:', e); 6}); 7 8channel().listenForWhisper('typing', (e) => { 9 console.log(e.name);10});11</script>
通知
透過將事件廣播與 通知 結合使用,JavaScript 應用可以在無需重新整理頁面的情況下接收新通知。在開始之前,請務必閱讀關於使用 廣播通知頻道 的文件。
配置好通知使用廣播頻道後,可以使用 Echo 的 notification 方法監聽廣播事件。請記住,頻道名稱應與接收通知的實體的類名相匹配:
1Echo.private(`App.Models.User.${userId}`)2 .notification((notification) => {3 console.log(notification.type);4 });
1import { useEchoModel } from "@laravel/echo-react";2 3const { channel } = useEchoModel('App.Models.User', userId);4 5channel().notification((notification) => {6 console.log(notification.type);7});
1<script setup lang="ts">2import { useEchoModel } from "@laravel/echo-vue";3 4const { channel } = useEchoModel('App.Models.User', userId);5 6channel().notification((notification) => {7 console.log(notification.type);8});9</script>
在此示例中,所有透過 broadcast 頻道傳送給 App\Models\User 例項的通知都將被回撥接收。應用 routes/channels.php 檔案中已包含 App.Models.User.{id} 頻道的頻道授權回撥。
停止監聽通知
如果你想停止監聽通知而不 離開頻道,可以使用 stopListeningForNotification 方法:
1const callback = (notification) => { 2 console.log(notification.type); 3} 4 5// Start listening... 6Echo.private(`App.Models.User.${userId}`) 7 .notification(callback); 8 9// Stop listening (callback must be the same)...10Echo.private(`App.Models.User.${userId}`)11 .stopListeningForNotification(callback);