翻譯進度
46.22% 已翻譯
更新時間:
2024年6月30日 上午8:17:00 [世界標準時間]
翻譯人員:
幫我們翻譯此頁

廣播 - Broadcast

簡介

在許多現代 Web App 中,都使用了 WebSocket 來提供即時更新的 UI。當某個資料在伺服器上被更新,通常會通過 WebSocket 連線來將一個訊息傳送給用戶端做處理。比起不斷從伺服器上拉取資料並反應到 UI 上,WebSocket 提供是更有效率的方案。

舉例來說,假設我們的 App 可以將使用者資料匯出為 CSV 檔並以電子郵件寄出。不過,建立 CSV 檔需要數分鐘的事件,因此我們選擇將建立與寄送 CSV 檔的程式放在佇列任務中。當 CSV 當建立完畢並寄給使用者後,我們可以使用「事件廣播」來將 App\Events\UserDataExported 事件分派給應用程式的 JavaScript。收到事件後,使用者就能在不重新整理的情況下看到一個訊息,表示我們已將 CSV 檔寄送出去。

為了協助你製作這種類型的功能,Laravel 讓你能簡單地將伺服器端 Laravel 事件通過 WebSocket 連線來「廣播」出去。通過廣播 Laravel 事件,就可以在伺服器端 Laravel 程式與用戶端 JavaScript 程式間共享相同的事件名稱與資料。

「廣播」背後的核心概念很簡單:用戶端會在前端連線到一個有名稱的頻道,而後端 Laravel 網站則會將事件廣播給這些頻道。這些事件可以包含任何你想讓前端存取的額外資料。

支援的 Driver

By default, Laravel includes three server-side broadcasting drivers for you to choose from: Laravel Reverb, Pusher Channels, and Ably.

lightbulb

在深入探討事件廣播前,請先確保你已閱讀有關 事件與監聽程式的 Laravel 說明文件。

伺服器端安裝

若要開始使用 Laravel 的事件廣播,我們需要在 Laravel 專案中做一些設定以及安裝一些套件。

事件廣播是通過伺服器端的廣播 Driver 將 Laravel 事件廣播出去,讓 Laravel Echo (一個 JavaScript 套件) 可以在瀏覽器用戶端內接收這個事件。別擔心 —— 我們會一步一步地介紹安裝過程的每一部分。

設定

All of your application's event broadcasting configuration is stored in the config/broadcasting.php configuration file. Laravel supports several broadcast drivers out of the box: Laravel Reverb, Pusher Channels, Ably, and a log driver for local development and debugging. Additionally, a null driver is included which allows you to totally disable broadcasting during testing. A configuration example is included for each of these drivers in the config/broadcasting.php configuration file.

Installation

By default, broadcasting is not enabled in new Laravel applications. You may enable broadcasting using the install:broadcasting Artisan command:

1php artisan install:broadcasting
1php artisan install:broadcasting

The install:broadcasting command will create a routes/channels.php file where you may register your application's broadcast authorization routes and callbacks.

設定佇列

Before broadcasting any events, you should first configure and run a queue worker. All event broadcasting is done via queued jobs so that the response time of your application is not seriously affected by events being broadcast.

Reverb

When running the install:broadcasting command, you will be prompted to install Laravel Reverb. Of course, you may also install Reverb manually using the Composer package manager. Since Reverb is currently in beta, you will need to explicitly install the beta release:

1composer require laravel/reverb:@beta
1composer require laravel/reverb:@beta

Once the package is installed, you may run Reverb's installation command to publish the configuration, add Reverb's required environment variables, and enable event broadcasting in your application:

1php artisan reverb:install
1php artisan reverb:install

You can find detailed Reverb installation and usage instructions in the Reverb documentation.

Reverb

You may install Reverb using the Composer package manager. Since Reverb is currently in beta, you will need to explicitly install the beta release:

1composer require laravel/reverb:@beta
1composer require laravel/reverb:@beta

Once the package is installed, you may run Reverb's installation command to publish the configuration, update your applications's broadcasting configuration, and add Reverb's required environment variables:

1php artisan reverb:install
1php artisan reverb:install

You can find detailed Reverb installation and usage instructions in the Reverb documentation.

Pusher Channels

若有打算要使用 Pusher Channels,那麼應通過 Composer 套件管理員來安裝 Pusher Channels 的 PHP SDK:

1composer require pusher/pusher-php-server
1composer require pusher/pusher-php-server

Next, you should configure your Pusher Channels credentials in the config/broadcasting.php configuration file. An example Pusher Channels configuration is already included in this file, allowing you to quickly specify your key, secret, and application ID. Typically, you should configure your Pusher Channels credentials in your application's .env file:

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"
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,如簇集 (Cluster)。

Then, set the BROADCAST_CONNECTION environment variable to pusher in your application's .env file:

1BROADCAST_CONNECTION=pusher
1BROADCAST_CONNECTION=pusher

最後,就可以安裝並設定 Laravel Echo。Laravel Echo 會在用戶端上接收廣播事件。

Ably

lightbulb

下方的說明文件討論了如何在「Pusher 相容模式 (Pusher Compatibility)」下使用 Ably。不過,Ably 團隊推薦並維護了一個 Broadcaster 程式,以及一個可使用 Ably 特別功能的 Echo 用戶端。更多有關使用 Ably 維護的 Driver 的資訊,請參考 Ably 的 Laravel Broadcaster 說明文件 (英語)

若有打算要使用 Ably,則請使用 Composer 套件管理員來安裝 Ably 的 PHP SDK:

1composer require ably/ably-php
1composer require ably/ably-php

接著,應在 config/broadcasting.php 設定檔中設定 Pusher Channels 的憑證。該檔案中已經有包含了一個範例的 Ably 設定,讓你可以快速指定你的金鑰。通常來說,這個值應該要通過 ABLY_KEY 環境變數 來設定:

1ABLY_KEY=your-ably-key
1ABLY_KEY=your-ably-key

Then, set the BROADCAST_CONNECTION environment variable to ably in your application's .env file:

1BROADCAST_CONNECTION=ably
1BROADCAST_CONNECTION=ably

最後,就可以安裝並設定 Laravel Echo。Laravel Echo 會在用戶端上接收廣播事件。

用戶端安裝

Reverb

Laravel Echo is a JavaScript library that makes it painless to subscribe to channels and listen for events broadcast by your server-side broadcasting driver. You may install Echo via the NPM package manager. In this example, we will also install the pusher-js package since Reverb utilizes the Pusher protocol for WebSocket subscriptions, channels, and messages:

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

Once Echo is installed, you are ready to create a fresh Echo instance in your application's JavaScript. A great place to do this is at the bottom of the resources/js/bootstrap.js file that is included with the Laravel framework. By default, an example Echo configuration is already included in this file - you simply need to uncomment it and update the broadcaster configuration option to 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});
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});

Next, you should compile your application's assets:

1npm run build
1npm run build
lightbulb

The Laravel Echo reverb broadcaster requires laravel-echo v1.16.0+.

Pusher Channels

Laravel Echo is a JavaScript library that makes it painless to subscribe to channels and listen for events broadcast by your server-side broadcasting driver. Echo also leverages the pusher-js NPM package to implement the Pusher protocol for WebSocket subscriptions, channels, and messages.

The install:broadcasting Artisan command automatically installs the laravel-echo and pusher-js packages for you; however, you may also install these packages manually via NPM:

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

Once laravel-echo and pusher-js are installed, you are ready to create a fresh Echo instance in your application's JavaScript. The install:broadcasting Artisan command creates a resources/js/echo.js file that handles this for you:

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

Next, you only need to compile your application's assets:

1npm run build
1npm run build
lightbulb

要瞭解更多有關編譯應用程式 JavaScript 素材的資訊,請參考 Vite 中的說明文件。

Pusher Channels

Laravel Echo is a JavaScript library that makes it painless to subscribe to channels and listen for events broadcast by your server-side broadcasting driver. Echo also leverages the pusher-js NPM package to implement the Pusher protocol for WebSocket subscriptions, channels, and messages.

The install:broadcasting Artisan command automatically installs the laravel-echo and pusher-js packages for you; however, you may also install these packages manually via NPM:

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

Once Echo is installed, you are ready to create a fresh Echo instance in your application's JavaScript. The install:broadcasting command creates an Echo configuration file at resources/js/echo.js; however, the default configuration in this file is intended for Laravel Reverb. You may copy the configuration below to transition your configuration to 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});
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});

Next, you should define the appropriate values for the Pusher environment variables in your application's .env file. If these variables do not already exist in your .env file, you should add them:

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}"
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}"

Once you have adjusted the Echo configuration according to your application's needs, you may compile your application's assets:

1npm run build
1npm run build
lightbulb

要瞭解更多有關編譯應用程式 JavaScript 素材的資訊,請參考 Vite 中的說明文件。

Using an Existing Client Instance

若已經有預先設定好的 Pusher Channels 用戶端實體,並想讓 Echo 使用的話,可以將其傳入 Echo 的 client 設定選項:

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

lightbulb

下方的說明文件討論了如何在「Pusher 相容模式 (Pusher Compatibility)」下使用 Ably。不過,Ably 團隊推薦並維護了一個 Broadcaster 程式,以及一個可使用 Ably 特別功能的 Echo 用戶端。更多有關使用 Ably 維護的 Driver 的資訊,請參考 Ably 的 Laravel Broadcaster 說明文件 (英語)

Laravel Echo is a JavaScript library that makes it painless to subscribe to channels and listen for events broadcast by your server-side broadcasting driver. Echo also leverages the pusher-js NPM package to implement the Pusher protocol for WebSocket subscriptions, channels, and messages.

The install:broadcasting Artisan command automatically installs the laravel-echo and pusher-js packages for you; however, you may also install these packages manually via NPM:

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

在繼續之前,應先在 Ably 應用程式設定中啟用 Pusher 通訊協定。可以在 Ably 應用程式設定面板中的「Protocol Adapter Settings」這個部分內啟用此功能。

Once Echo is installed, you are ready to create a fresh Echo instance in your application's JavaScript. The install:broadcasting command creates an Echo configuration file at resources/js/echo.js; however, the default configuration in this file is intended for Laravel Reverb. You may copy the configuration below to transition your configuration to 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});
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});

You may have noticed our Ably Echo configuration references a VITE_ABLY_PUBLIC_KEY environment variable. This variable's value should be your Ably public key. Your public key is the portion of your Ably key that occurs before the : character.

Once you have adjusted the Echo configuration according to your needs, you may compile your application's assets:

1npm run dev
1npm run dev
lightbulb

要瞭解更多有關編譯應用程式 JavaScript 素材的資訊,請參考 Vite 中的說明文件。

概念概覽

Laravel 的事件廣播功能能讓你以基於 Driver 的方法來將伺服器端的 Laravel 事件通過 WebSockets 廣播到用戶端 JavaScript 上。目前,Laravel 隨附了 Pusher ChannelsAbly 兩個 Driver。可以在用戶端使用 Laravel Echo JavaScript 套件來輕鬆取得事件。

事件是通過「頻道 (Channel)」進行廣播的,頻道可以被設為公共或私有。任何網站的瀏覽者都可以在不登入或經過授權的情況下訂閱公開頻道。不過,如果要訂閱私有頻道,就必須要登入並經過授權才可以監聽該頻道。

Using an Example Application

在深入探討事件廣播的各個元件之前,我們先來用網路商店當作例子,以高階的角度來個概覽。

In our application, let's assume we have a page that allows users to view the shipping status for their orders. Let's also assume that an OrderShipmentStatusUpdated event is fired when a shipping status update is processed by the application:

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

ShouldBroadcast 介面

我們並不希望使用者在檢視某個訂單的時候還需要重新整理整個頁面才能看到狀態更新;我們希望在訂單更新建立的時候就能廣播給專案。因此,我們需要將 OrderShipmentStatusUpdated 事件標上 ShouldBroadcast 介面。通過加上該介面,就能告訴 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\Order
18 */
19 public $order;
20}
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\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}
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}

若想要讓 Event 被 Broadcast 到多個 Channel,可以回傳一組 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}
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});
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 方法接收 2 個引數:頻道的名稱,以及會回傳 truefalse 的回呼。這個回呼用來判斷使用者是否已授權監聽此頻道。

所有的授權回呼都會收到目前登入使用者作為其第一個引數,而接下來的引數則是其他額外的萬用字元參數。在這個例子中,我們使用了 {orderId} 預留位置來標示頻道名稱中的「ID」部分是萬用字元。

Listening for Event Broadcasts

接著,剩下的工作就是在 JavaScript 程式碼內監聽事件了。我們可以使用 Laravel Echo。首先,我們要先用 private 方法來監聽私有頻道。接著,可以監聽「listenOrderShipmentStatusUpdated 事件。預設情況下,該事件的所有公共屬性都會被包含在廣播事件內:

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

定義廣播事件

為了告訴 Laravel 應廣播某個給定的事件,我們必須要在事件類別上實作 Illuminate\Contracts\Broadcasting\ShouldBroadcast 介面。在所有產生出來的事件類別上,框架已經幫你引入這個介面了,因此你可以輕鬆地將該介面加至任何事件上。

ShouldBroadcast 介面只要求實作單一方法:broadcastOnbroadcastOn 方法應回傳一個頻道,或是一個包含頻道的陣列。這些頻道是事件要進行廣播的頻道。頻道應為 Channel, PrivateChannelPresenceChannel 的實體。Channel 的實體代表任何使用者都能監聽的公共頻道,而 PrivateChannelPresenceChannels 代表需要進行頻道授權的私有頻道:

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}
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 介面後,只需要像平常一樣觸發事件即可。事件被觸發後,佇列任務會自動通過指定的 Broadcast Driver 來廣播事件。

Broadcast 名稱

預設情況下,Laravel 會使用事件的類別名來進行廣播。不過,也可以在事件上定義 broadcastAs 方法來自訂 Broadcast 名稱:

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

若使用 broadcastAs 方法來自訂 Broadcast 名稱,則應確保註冊監聽程式時有加上前置 . 字元。加上該前置字元可用來告訴 Echo 不要在事件前方加上專案的命名空間:

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

Broadcast 資料

廣播事件時,事件所有的 public 屬性都會被自動序列化,並作為事件的 Payload 進行廣播,讓你能在 JavaScript 程式碼中存取事件的所有公共資料。因此,舉例來說,假設我們的事件有一個 public $user 屬性,其中包含了 Eloquent Model,那麼事件的 Broadcast Payload 會是:

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

不過,若想對 Broadcast Payload 進一步地控制,可以在事件內加上一個 broadcastWith 方法。這個方法應回傳一個陣列,包含要作為事件 Payload 使用的資料:

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

Broadcast 佇列

預設情況下,所有的廣播事件都會使用 queue.php 設定檔中的預設佇列連連。可以通過在事件類別內定義 queue 屬性來自訂 Broadcaster 要使用的佇列連線名稱:

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';
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}
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 佇列來代替預設的佇列 Driver,可以使用 ShouldBroadcastNow 來代替 ShouldBroadcast 進行實作:

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

Broadcast 條件

有時候我們可能只想在滿足給定條件的時候才廣播事件。可以通過在事件類別上新增 broadcastWhen 方法來在其中定義這些條件:

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

Broadcasting and Database Transactions

當廣播事件是在資料庫 Transaction 內分派的時候,這個事件可能會在資料庫 Transaction 被 Commit 前被佇列進行處理了。發生這種情況時,在資料庫 Transaction 期間對 Model 或資料庫記錄所做出的更新可能都還未反應到資料庫內。另外,所有在 Transaction 期間新增的 Model 或資料庫記錄也可能還未出現在資料庫內。若事件有依賴這些 Model 的話,在處理廣播事件的任務時可能會出現未預期的錯誤。

即使佇列連線的 after_commit 設定選項被設為 false,還是可以通過在 Event 類別上實作 ShouldDispatchAfterCommit 介面來讓 Laravel 知道要在所有開啟的資料庫 Transaction 被 Commit 後廣播該 Event:

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}
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}
lightbulb

要瞭解更多有關這類問題的解決方法,請參考有關佇列任務與資料庫 Transaction 有關的說明文件。

授權頻道

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

When broadcasting is enabled, Laravel automatically registers the /broadcasting/auth route to handle authorization requests. The /broadcasting/auth route is automatically placed within the web middleware group.

定義授權回呼

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

1use App\Models\User;
2 
3Broadcast::channel('orders.{orderId}', function (User $user, int $orderId) {
4 return $user->id === Order::findOrNew($orderId)->user_id;
5});
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 方法接收 2 個引數:頻道的名稱,以及會回傳 truefalse 的回呼。這個回呼用來判斷使用者是否已授權監聽此頻道。

所有的授權回呼都會收到目前登入使用者作為其第一個引數,而接下來的引數則是其他額外的萬用字元參數。在這個例子中,我們使用了 {orderId} 預留位置來標示頻道名稱中的「ID」部分是萬用字元。

可以使用 channel:list Artisan 指令來檢視專案中所有 Broadcast 身分驗證回呼的列表:

1php artisan channel:list
1php artisan channel:list

授權回呼的 Model

就像 HTTP 路由一樣,頻道路由也能使用顯式或隱式路由 Model 的功能。舉例來說,可以不接收字串或數字的 Order ID,而要求實際的 Order Model 實體:

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

與 HTTP 路由 Model 綁定不同,頻道的 Model 綁定不支援自動[為隱式 Model 綁定加上作用域]。不過,通常來說這不會造成問題,因為大部分的頻道都可以被放置與單一 Model 的獨立主鍵作用域內。

授權回呼認證

私有與 Presence 廣播頻道會通過專案預設的認證 Guard 來認證目前的使用者。若使用者未登入,則頻道認證會自動拒絕,且授權回呼永遠不會被執行。不過,若有需要,也可以指定多個自訂 Guard 來認證連入請求:

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

定義 Channel 類別

若你的專案會使用到許多不同的頻道,則 routes/channels.php 可能會變得很肥大。因此,比起使用閉包來授權頻道,我們可以改用頻道類別。要建立頻道類別,請使用 make:channel Artisan 指令。這個指令會在 app/Broadcasting 目錄內放置一個新的頻道類別。

1php artisan make:channel OrderChannel
1php artisan make:channel OrderChannel

接著,在 routes/channels.php 檔案內註冊頻道:

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

最後,可以將頻道的授權邏輯放在頻道類別的 join 方法內。這個 join 方法用來放置與平常放在頻道授權閉包相同的邏輯。也可以使用頻道 Model 綁定:

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 }
17 
18 /**
19 * Authenticate the user's access to the channel.
20 */
21 public function join(User $user, Order $order): array|bool
22 {
23 return $user->id === $order->user_id;
24 }
25}
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 }
17 
18 /**
19 * Authenticate the user's access to the channel.
20 */
21 public function join(User $user, Order $order): array|bool
22 {
23 return $user->id === $order->user_id;
24 }
25}
lightbulb

與 Laravel 內其他類別一樣,頻道類別也會自動由 Service Container 解析。因此,我們可以在頻道的建構函式上對任何所需要的依賴進行型別提示。

廣播事件

定義好事件並將其以 ShouldBroadcast 介面進行標示後,我們只需要使用 dispatch 方法來觸發事件即可。事件觸發程式會注意到這個事件有被標註為 ShouldBroadcast 介面,並將事件放入佇列以進行廣播:

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

Only to Others

在建立一個有使用到事件廣播的專案時,我們可能會需要將某個事件廣播給除了目前使用者以外的所有頻道訂閱者。可以通過 broadcast 輔助函式以及 toOthers 方法來完成:

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

為了幫助你更容易理解什麼時候會需要用到 toOthers 方法,我們來假設有個任務清單 App。在這個 App 中,使用者可以輸入任務名稱來新增任務。為了建立任務,這個 App 可能會向 /task URL 發起一個請求,該請求會將任務的建立廣播出去,並回傳代表新任務的 JSON。當 JavaScript 端從這個 End-point 收到回覆後,就可以直接將新任務插入到任務清單內。像這樣:

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

不過,提醒一下,我們也會將任務的建立廣播出去。如果 JavaScript 端也會監聽這個事件來將任務新增到任務清單上,那麼列表上就會有重複的任務:一個是從 End-point 回傳回來的,另一個則是從監聽事件來的。我們可以通過使用 toOthers 方法來告訴廣播程式不要將該事件廣播給目前的使用者。

lightbulb

若要呼叫 toOthers 方法,該事件必須要 use Illuminate\Broadcasting\InteractsWithSockets Trait。

設定

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

若你未使用全域 Axios 實體,則需要手動設定 JavaScript 端來在所有外連請求上傳送 X-Socket-ID 標頭。可以通過 Echo.socketId 方法來取得 Socket ID:

1var socketId = Echo.socketId();
1var socketId = Echo.socketId();

Customizing the Connection

若你的專案與許多不同的廣播連線互動時,如果我們想使用預設廣播程式以外的特定廣播程式來廣播事件,則可以使用 via 方法來指定要將事件推送給哪個連線:

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

或者,也可以通過在事件的建構函式 (Constructor) 內呼叫 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}
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}

接收廣播

Listening for Events

安裝並設定好 Laravel Echo 後,就可以開始監聽來自 Laravel 端廣播的事件了。首先,使用 channel 方法來取得頻道的實體,然後呼叫 listen 方法來監聽某個特定的事件:

1Echo.channel(`orders.${this.order.id}`)
2 .listen('OrderShipmentStatusUpdated', (e) => {
3 console.log(e.order.name);
4 });
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(/* ... */);
1Echo.private(`orders.${this.order.id}`)
2 .listen(/* ... */)
3 .listen(/* ... */)
4 .listen(/* ... */);

Stop Listening for Events

若想在不離開頻道的情況下停止監聽給定的事件,可以使用 stopListening 方法:

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

Leaving a Channel

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

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

若要離開頻道以及其關聯的私有與 Presence 頻道,可以呼叫 leave 方法:

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

命名空間 (Namespace)

你可能已經注意到,我們並沒有為事件類別指定完整的 App\Events 命名空間。這是因為,Echo 會自動假設事件都放在 App\Events 命名空間下。不過,我們可以在初始化 Echo 時傳入 namespace 設定選項來設定要使用的根命名空間:

1window.Echo = new Echo({
2 broadcaster: 'pusher',
3 // ...
4 namespace: 'App.Other.Namespace'
5});
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 });
1Echo.channel('orders')
2 .listen('.Namespace\\Event\\Class', (e) => {
3 // ...
4 });

Presence 頻道

Presence 頻道擁有私有頻道的安全性,且會提供該頻道的訂閱者等額外資訊。這樣一來便能輕鬆地建立強大的協作 App 功能,如提示目前使用者由其他人正在檢視相同頁面,或是列出聊天室中的使用者狀態。

授權 Presence 頻道

所有的 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});
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 方法會與所暴露的 listen 方法一起回傳一個 PresenceChannel 的實作,這樣一來你就能訂閱 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 });
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 });

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

Broadcasting to Presence Channels

Presence 頻道可以像公用或私有頻道一樣接收事件。以聊天室為例,我們可能像廣播 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}
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 方法來排除目前使用者接收該 Broadcast:

1broadcast(new NewMessage($message));
2 
3broadcast(new NewMessage($message))->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 });
1Echo.join(`chat.${roomId}`)
2 .here(/* ... */)
3 .joining(/* ... */)
4 .leaving(/* ... */)
5 .listen('NewMessage', (e) => {
6 // ...
7 });

Model 廣播

lightbulb

在進一步閱讀有關 Model 廣播的說明文件前,我們建議讀者先瞭解有關 Laravel 的 Model 廣播服務以及如何手動建立並監聽廣播時間的一般概念。

在專案的 Eloquent Model 被建立、更新、或刪除時,我們常常會廣播事件。當然,我們可以手動為 Eloquent Model 的狀態更改定義自訂事件並將這些事件標記為 ShouldBroadcast 來輕鬆達成:

不過,我們讓事件在專案中負責其他功能,那麼如果只建立一個用來廣播的事件就很麻煩。為了解決這個問題,再 Laravel 中,我們可以讓 Eloquent Model 自動將其狀態更改廣播出去:

要開始設定自動廣播,應在 Eloquent Model 上使用 Illuminate\Database\Eloquent\BroadcastsEvents Trait。此外,該 Model 應定義一個 broadcastOn 方法,並在其中回傳一組包含頻道名稱的陣列,以供 Model 事件廣播:

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

再 Model 中包含該 Trait 並定義好廣播頻道後,當 Model 實體被建立、更新、刪除、軟刪除、或是取消軟刪除後自動廣播事件。

此外,讀者可能已經發現,broadcastOn 方法接收了一個字串的 $event 引述。這個引述包含了 Model 上所發生的事件,其值為 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}
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}

自訂 Model 廣播的事件建立

有時候,我們可能會想自訂 Laravel 要如何建立 Model 廣播時使用的事件。為此,我們可以通過在 Eloquent Model 上定義一個 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}
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}

Model 廣播慣例

頻道慣例

讀者可能已經發現,在上方的 Model 範例中,broadcastOn 方法並沒有回傳 Channel 實體,而是直接回傳 Eloquent Model。若 Model 的 broadcastOn 方法回傳的是 Model 實體 (或是包含 Model 實體的陣列),則 Laravel 會使用該 Model 的類別名稱與主索引鍵識別元作為頻道名稱,自動為該 Model 初始化一個私人頻道。

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

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

若有打算要從 Model 的 broadcastOn 方法內明顯回傳頻道實體,則可以將 Eloquent Model 實體傳入該頻道的建構函式。這樣一來,Laravel 就可以通過剛才提到的 Model 頻道慣例來將 Eloquent Model 轉換為頻道名稱字串:

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

若想判斷某個 Model 的頻道名稱,可以在任何 Model 實體上呼叫 broadcastChannel 方法。舉例來說,對於一個 id1App\Models\User Model,該方法會回傳一個字串 App.Models.User.1

1$user->broadcastChannel()
1$user->broadcastChannel()

事件慣例

由於 Model 廣播事件並不與專案的 App\Events 目錄內的「真實」事件有關,這些事件只會依據慣例來指派名稱與 Payload (裝載)。Laravel 的慣例就是使用 Model 的類別名稱 (不含 Namespace) 與觸發廣播的 Model 事件來廣播。

因此,對 App\Models\Post Model 進行更新,會將 PostUpdated 事件與下列 Payload 廣播到用戶端:

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

刪除 App\Models\Post Model 時廣播的事件名稱會是 UserDeleted

若有需要,也可以通過在 Model 中新增一個 broadcastAsbroadcastWith 方法來自訂廣播的名稱與 Payload。這些方法會收到目前發生的 Model 事件或動作,好讓我們能為不同的 Model 動作自訂事件名稱與 Payload。若在 broadcastAs 方法中回傳 null,則 Laravel 會使用上方討論過的 Model 廣播事件名稱的慣例來廣播這個事件:

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

Listening for Model Broadcasts

在 Model 中新增好 BroadcastsEvents Trait 並定義好 Model 的 broadcastOn 方法後,就可以開始在用戶端中監聽廣播出來的 Model 事件了。在開始前,建議你先閱讀有關監聽事件的完整說明文件。

首先,使用 private 方法來取得 Channel 實體,然後呼叫 listen 方法來監聽特定的事件。一般來說,傳給 private 方法的頻道名稱應與 Laravel 的 Model 廣播慣例相對應。

取得 Channel 實體後,就可以使用 listen 方法來監聽特定的事件。由於 Model 廣播事件並不與專案中 App\Events 目錄下的「真實事件」互相關聯,因此,事件名稱前應加上一個 . 字元,以標識其不屬於特定的命名空間。每個 Model 廣播事件都有一個 model 屬性,其中包含了 Model 中所有可廣播的屬性:

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

用戶端事件

lightbulb

在使用 Pusher Channels 時,可以在 Application Dashboard 內啟用「App Settings」中的「Client Event」,以傳送用戶端事件。

有時候我們可能會想將事件直接廣播給其他連線的用戶端,而不經由 Laravel 端。特別像是如顯示「正在輸入」等通知時,我們只是想告訴使用者網站內的其他使用者正在特定畫面上輸入。

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

1Echo.private(`chat.${roomId}`)
2 .whisper('typing', {
3 name: this.user.name
4 });
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 });
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 });
1Echo.private(`App.Models.User.${userId}`)
2 .notification((notification) => {
3 console.log(notification.type);
4 });

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

翻譯進度
46.22% 已翻譯
更新時間:
2024年6月30日 上午8:17:00 [世界標準時間]
翻譯人員:
幫我們翻譯此頁

留言

尚無留言

“Laravel” is a Trademark of Taylor Otwell.
The source documentation is released under MIT license. See laravel/docs on GitHub for details.
The translated documentations are released under MIT license. See cornch/laravel-docs-l10n on GitHub for details.