Giới thiệu

Trong nhiều application hoặc web hiện đại, WebSockets được sử dụng để thực hiện cập nhật trực tiếp lên giao diện người dùng theo thời gian thực. Sau khi dữ liệu được cập nhật lên máy chủ, thì một thông báo cũng sẽ được gửi qua kết nối WebSocket để được phía client xử lý. WebSockets cung cấp một sự thay thế mạnh mẽ và hiệu quả hơn để liên tục đồng bộ máy chủ của ứng dụng của bạn cho các thay đổi dữ liệu sẽ được phản ánh lên giao diện người dùng.

Ví dụ: hãy tưởng tượng ứng dụng của bạn có thể export dữ liệu của người dùng sang file CSV và gửi email cho họ. Tuy nhiên, việc tạo file CSV này mất vài phút nên bạn chọn cách tạo và gửi file CSV qua mail trong một queued job. Khi CSV đã được tạo và gửi qua mail cho người dùng, chúng ta có thể sử dụng broadcasting để gửi một event App\Events\UserDataExported mà JavaScript của ứng dụng của chúng ta nhận được. Sau khi nhận được event, chúng ta có thể hiển thị thông báo cho người dùng là file CSV của họ đã được gửi qua email và họ không cần phải refresh lại trang.

Để hỗ trợ bạn trong việc xây dựng các loại chức năng này, Laravel giúp bạn dễ dàng "broadcast" Laravel events phía máy chủ của bạn qua kết nối WebSocket. Broadcasting các event Laravel của bạn cho phép bạn chia sẻ cùng tên event và dữ liệu giữa ứng dụng Laravel bên server và ứng dụng JavaScript phía client của bạn.

Các khái niệm cốt lõi đằng sau việc broadcasting rất đơn giản: các client kết nối với các channel được đặt tên trong giao diện người dùng, trong khi ứng dụng Laravel của bạn broadcast các event tới các channel này trong phần backend. Những event này có thể chứa bất kỳ dữ liệu nào mà bạn muốn cung cấp cho giao diện người dùng.

Supported Drivers

Mặc định, Laravel có chứa ba driver broadcasting cho server-side để bạn lựa chọn: Laravel Reverb, Pusher Channels, và Ably.

[!NOTE] Trước khi đi sâu vào broadcasting event, hãy đảm bảo là bạn đã đọc tài liệu của Laravel về event và listener.

Bắt đầu nhanh

Mặc định, broadcasting không được enable trong các ứng dụng Laravel mới. Bạn có thể enable broadcasting bằng cách sử dụng lệnh Artisan install:broadcasting:

php artisan install:broadcasting

Lệnh install:broadcasting sẽ hỏi bạn chọn dịch vụ broadcast event nào mà bạn muốn sử dụng. Ngoài ra, nó sẽ tạo một file cấu hình config/broadcasting.php và một file routes/channels.php nơi mà bạn có thể đăng ký các route và các callback xác thực broadcast cho ứng dụng của bạn.

Mặc định, Laravel hỗ trợ một số broadcast driver: Laravel Reverb, Pusher Channels, Ably, và driver log dành cho lúc phát triển và lúc gỡ lỗi. Ngoài ra, driver null cũng được cung cấp cho phép bạn tắt broadcasting trong khi test. Một số cấu hình mẫu cũng sẽ được cung cấp trong file cấu hình config/broadcasting.php.

Tất cả các cấu hình event broadcasting cho ứng dụng của bạn được lưu trong file cấu hình config/broadcasting.php. Bạn đừng lo nếu file này chưa tồn tại trong ứng dụng của bạn; nó sẽ được tạo khi bạn chạy lệnh Artisan install:broadcasting.

Next Steps

Sau khi bạn đã enable event broadcasting, bạn đã sẵn sàng tìm hiểu thêm về việc định nghĩa các broadcast eventlisten các event này. Nếu bạn đang sử dụng các starter kit React, Vue hoặc Svelte của Laravel, bạn có thể listen các event bằng cách sử dụng hook useEcho của Echo.

[!NOTE] Trước khi broadcast bất kỳ event nào, đầu tiên bạn nên cấu hình và chạy một queue worker. Tất cả các event broadcasting đều được thực hiện thông qua các queued job sẽ giúp thời gian response của ứng dụng của bạn không bị ảnh hưởng bởi các event đang được broadcast.

Server Side Installation

Để bắt đầu sử dụng Laravel's event broadcasting, chúng ta cần thực hiện một số cấu hình trong ứng dụng Laravel cũng như cài đặt một số package.

Việc broadcasting event được thực hiện bởi một driver broadcasting phía server, driver này sẽ broadcast các event Laravel để Laravel Echo (một thư viện JavaScript) có thể nhận được chúng ở bên trong trình duyệt của client. Đừng lo lắng - chúng ta sẽ đi qua từng bước của quá trình cài đặt này.

Reverb

Để nhanh chóng enable các tính năng broadcasting của Laravel khi sử dụng Reverb làm driver broadcast event của bạn, hãy chạy lệnh Artisan install:broadcasting với tùy chọn --reverb. Lệnh Artisan này sẽ cài đặt các package Composer và NPM cần thiết của Reverb và cập nhật file .env của ứng dụng với các biến môi trường phù hợp:

php artisan install:broadcasting --reverb

Manual Installation

Khi chạy lệnh install:broadcasting, bạn sẽ được nhắc là cần cài đặt Laravel Reverb. Tất nhiên, bạn cũng có thể cài đặt Reverb bằng trình quản lý package Composer:

composer require laravel/reverb

Sau khi package được cài đặt xong, bạn có thể chạy lệnh cài đặt của Reverb để export ra cấu hình, thêm các biến môi trường cần thiết của Reverb, và enable broadcast trong ứng dụng của bạn:

php artisan reverb:install

Bạn có thể tìm thấy hướng dẫn cài đặt và sử dụng Reverb chi tiết hơn trong tài liệu Reverb.

Pusher Channels

Để nhanh chóng enable các tính năng broadcasting của Laravel khi sử dụng Pusher làm driver broadcast event của bạn, hãy chạy lệnh Artisan install:broadcasting với tùy chọn --pusher. Lệnh Artisan này sẽ yêu cầu bạn nhập các thông tin xác thực của Pusher, cài đặt các SDK PHP và JavaScript của Pusher, và cập nhật file .env của ứng dụng với các biến môi trường phù hợp:

php artisan install:broadcasting --pusher

Manual Installation

Để cài đặt Pusher thủ công, bạn cần cài đặt SDK PHP của Pusher Channels bằng trình quản lý package Composer:

composer require pusher/pusher-php-server

Tiếp theo, bạn nên cấu hình thông tin đăng nhập Pusher Channel của bạn trong file cấu hình config/broadcasting.php. Một ví dụ về cấu hình Pusher Channel đã được chứa trong file này, cho phép bạn nhanh chóng chỉ định khóa, secret và ID ứng dụng của bạn. Thông thường, bạn nên cấu hình thông tin xác thực Pusher Channels trong file .env của ứng dụng:

PUSHER_APP_ID="your-pusher-app-id"
PUSHER_APP_KEY="your-pusher-key"
PUSHER_APP_SECRET="your-pusher-secret"
PUSHER_HOST=
PUSHER_PORT=443
PUSHER_SCHEME="https"
PUSHER_APP_CLUSTER="mt1"

Cấu hình pusher của file config/broadcasting.php cũng cho phép bạn chỉ định thêm các options được hỗ trợ bởi Channel, chẳng hạn như cluster.

Sau đó, set biến môi trường BROADCAST_CONNECTION thành pusher trong file .env của ứng dụng:

BROADCAST_CONNECTION=pusher

Cuối cùng, bạn đã sẵn sàng để cài đặt và cấu hình Laravel Echo và sẽ nhận các broadcast event ở phía client.

Ably

[!NOTE] Tài liệu dưới đây sẽ thảo luận về cách dùng Ably trong chế độ "tương thích với Pusher". Tuy nhiên, Ably team rất khuyến khích bạn và duy trì một broadcaster, một Echo client có thể tận dụng tối đa các khả năng độc đáo do Ably cung cấp. Để biết thêm thông tin về cách sử dụng các driver được Ably cung cấp, vui lòng tham khảo tài liệu về broadcaster Laravel của Ably.

Để nhanh chóng enable các tính năng broadcasting của Laravel khi sử dụng Ably làm driver broadcast event của bạn, hãy chạy lệnh Artisan install:broadcasting với tùy chọn --ably. Lệnh Artisan này sẽ yêu cầu bạn nhập các thông tin xác thực của Ably, cài đặt các SDK PHP và JavaScript của Ably, và cập nhật file .env của ứng dụng với các biến môi trường phù hợp:

php artisan install:broadcasting --ably

Trước khi tiếp tục, bạn nên bật hỗ trợ giao thức Pusher trong phần cài đặt ứng dụng Ably của bạn. Bạn có thể bật tính năng này trong phần "Protocol Adapter Settings" của dashboard cài đặt ứng dụng Ably.

Manual Installation

Để cài đặt Ably thủ công, thì bạn nên cài đặt Ably PHP SDK bằng trình quản lý package Composer:

composer require ably/ably-php

Tiếp theo, bạn nên cấu hình thông tin đăng nhập Ably của bạn trong file cấu hình config/broadcasting.php. Một ví dụ cấu hình Ably đã được chứa trong file này, cho phép bạn nhanh chóng chỉ định khóa của bạn. Thông thường, giá trị này phải được set thông qua biến môi trường ABLY_KEY:

ABLY_KEY=your-ably-key

Sau đó, set biến môi trường BROADCAST_CONNECTION thành ably trong file .env của ứng dụng:

BROADCAST_CONNECTION=ably

Cuối cùng, bạn đã sẵn sàng để cài đặt và cấu hình Laravel Echo và sẽ nhận các broadcast event ở phía client.

Cài đặt phía Client

Reverb

Laravel Echo là một thư viện JavaScript giúp việc subscribe channel và lắng nghe các event broadcast được tạo bởi driver server-side broadcasting trở nên dễ dàng.

Khi cài đặt Laravel Reverb thông qua lệnh Artisan install:broadcasting, khung và cấu hình của Reverb và Echo sẽ được tự động thêm vào ứng dụng của bạn. Tuy nhiên, nếu bạn muốn tự cấu hình Laravel Echo, bạn có thể thực hiện theo các hướng dẫn bên dưới.

Manual Installation

Để tự cấu hình Laravel Echo cho frontend của ứng dụng, trước tiên hãy cài đặt package pusher-js vì Reverb sử dụng giao thức Pusher cho các subscription, channel và tin nhắn thông qua WebSocket:

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

Sau khi Echo được cài đặt xong, bạn đã sẵn sàng tạo một instance Echo mới trong JavaScript của ứng dụng. Một vị trí tuyệt vời để thực hiện việc này là ở cuối file resources/js/bootstrap.js được chứa sẵn trong framework Laravel:

import Echo from 'laravel-echo';

import Pusher from 'pusher-js';
window.Pusher = Pusher;

window.Echo = new Echo({
    broadcaster: 'reverb',
    key: import.meta.env.VITE_REVERB_APP_KEY,
    wsHost: import.meta.env.VITE_REVERB_HOST,
    wsPort: import.meta.env.VITE_REVERB_PORT ?? 80,
    wssPort: import.meta.env.VITE_REVERB_PORT ?? 443,
    forceTLS: (import.meta.env.VITE_REVERB_SCHEME ?? 'https') === 'https',
    enabledTransports: ['ws', 'wss'],
});
import { configureEcho } from "@laravel/echo-react";

configureEcho({
    broadcaster: "reverb",
    // key: import.meta.env.VITE_REVERB_APP_KEY,
    // wsHost: import.meta.env.VITE_REVERB_HOST,
    // wsPort: import.meta.env.VITE_REVERB_PORT,
    // wssPort: import.meta.env.VITE_REVERB_PORT,
    // forceTLS: (import.meta.env.VITE_REVERB_SCHEME ?? 'https') === 'https',
    // enabledTransports: ['ws', 'wss'],
});
import { configureEcho } from "@laravel/echo-vue";

configureEcho({
    broadcaster: "reverb",
    // key: import.meta.env.VITE_REVERB_APP_KEY,
    // wsHost: import.meta.env.VITE_REVERB_HOST,
    // wsPort: import.meta.env.VITE_REVERB_PORT,
    // wssPort: import.meta.env.VITE_REVERB_PORT,
    // forceTLS: (import.meta.env.VITE_REVERB_SCHEME ?? 'https') === 'https',
    // enabledTransports: ['ws', 'wss'],
});
import { configureEcho } from "@laravel/echo-svelte";

configureEcho({
    broadcaster: "reverb",
    // key: import.meta.env.VITE_REVERB_APP_KEY,
    // wsHost: import.meta.env.VITE_REVERB_HOST,
    // wsPort: import.meta.env.VITE_REVERB_PORT,
    // wssPort: import.meta.env.VITE_REVERB_PORT,
    // forceTLS: (import.meta.env.VITE_REVERB_SCHEME ?? 'https') === 'https',
    // enabledTransports: ['ws', 'wss'],
});

Tiếp theo, bạn nên compile các asset của ứng dụng:

npm run build

[!WARNING] Laravel Echo reverb broadcaster sẽ yêu cầu laravel-echo v1.16.0 trở lên.

Pusher Channels

Laravel Echo là một thư viện JavaScript giúp bạn dễ dàng đăng ký channel và lắng nghe các event do các driver broadcasting server-side của bạn.

Khi cài đặt hỗ trợ broadcasting thông qua lệnh Artisan install:broadcasting --pusher, các khung và cấu hình của Pusher và Echo sẽ được tự động thêm vào ứng dụng của bạn. Tuy nhiên, nếu bạn muốn tự cấu hình Laravel Echo, bạn có thể thực hiện theo các hướng dẫn bên dưới.

Manual Installation

Để tự cấu hình Laravel Echo cho frontend của ứng dụng, trước tiên hãy cài đặt các package laravel-echopusher-js sử dụng giao thức Pusher cho các subscription, channel và tin nhắn thông qua WebSocket:

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

Sau khi cài đặt Echo, bạn đã sẵn sàng tạo một instance Echo mới trong file resources/js/bootstrap.js của ứng dụng:

import Echo from 'laravel-echo';

import Pusher from 'pusher-js';
window.Pusher = Pusher;

window.Echo = new Echo({
    broadcaster: 'pusher',
    key: import.meta.env.VITE_PUSHER_APP_KEY,
    cluster: import.meta.env.VITE_PUSHER_APP_CLUSTER,
    forceTLS: true
});
import { configureEcho } from "@laravel/echo-react";

configureEcho({
    broadcaster: "pusher",
    // key: import.meta.env.VITE_PUSHER_APP_KEY,
    // cluster: import.meta.env.VITE_PUSHER_APP_CLUSTER,
    // forceTLS: true,
    // wsHost: import.meta.env.VITE_PUSHER_HOST,
    // wsPort: import.meta.env.VITE_PUSHER_PORT,
    // wssPort: import.meta.env.VITE_PUSHER_PORT,
    // enabledTransports: ["ws", "wss"],
});
import { configureEcho } from "@laravel/echo-vue";

configureEcho({
    broadcaster: "pusher",
    // key: import.meta.env.VITE_PUSHER_APP_KEY,
    // cluster: import.meta.env.VITE_PUSHER_APP_CLUSTER,
    // forceTLS: true,
    // wsHost: import.meta.env.VITE_PUSHER_HOST,
    // wsPort: import.meta.env.VITE_PUSHER_PORT,
    // wssPort: import.meta.env.VITE_PUSHER_PORT,
    // enabledTransports: ["ws", "wss"],
});
import { configureEcho } from "@laravel/echo-svelte";

configureEcho({
    broadcaster: "pusher",
    // key: import.meta.env.VITE_PUSHER_APP_KEY,
    // cluster: import.meta.env.VITE_PUSHER_APP_CLUSTER,
    // forceTLS: true,
    // wsHost: import.meta.env.VITE_PUSHER_HOST,
    // wsPort: import.meta.env.VITE_PUSHER_PORT,
    // wssPort: import.meta.env.VITE_PUSHER_PORT,
    // enabledTransports: ["ws", "wss"],
});

Tiếp theo, bạn nên định nghĩa các giá trị cho các biến môi trường Pusher trong file .env của ứng dụng. Nếu các biến này chưa có trong file .env, bạn nên thêm chúng vào:

PUSHER_APP_ID="your-pusher-app-id"
PUSHER_APP_KEY="your-pusher-key"
PUSHER_APP_SECRET="your-pusher-secret"
PUSHER_HOST=
PUSHER_PORT=443
PUSHER_SCHEME="https"
PUSHER_APP_CLUSTER="mt1"

VITE_APP_NAME="${APP_NAME}"
VITE_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
VITE_PUSHER_HOST="${PUSHER_HOST}"
VITE_PUSHER_PORT="${PUSHER_PORT}"
VITE_PUSHER_SCHEME="${PUSHER_SCHEME}"
VITE_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"

Sau khi bạn đã điều chỉnh cấu hình Echo theo nhu cầu của ứng dụng bạn cần, bạn có thể biên dịch các asset của ứng dụng:

npm run build

[!NOTE] Để tìm hiểu thêm về cách biên dịch asset JavaScript cho ứng dụng của bạn, vui lòng tham khảo tài liệu về Vite.

Using An Existing Client Instance

Nếu bạn đã có một instance client Pusher Channel được cấu hình sẵn mà bạn muốn Echo sử dụng, bạn có thể truyền nó tới Echo thông qua tùy chọn cấu hình client:

import Echo from 'laravel-echo';
import Pusher from 'pusher-js';

const options = {
    broadcaster: 'pusher',
    key: import.meta.env.VITE_PUSHER_APP_KEY
}

window.Echo = new Echo({
    ...options,
    client: new Pusher(options.key, options)
});

Ably

[!NOTE] Tài liệu dưới đây sẽ thảo luận về cách dùng Ably trong chế độ "tương thích với Pusher". Tuy nhiên, Ably team rất khuyến khích bạn và duy trì một broadcaster, một Echo client có thể tận dụng tối đa các khả năng độc đáo do Ably cung cấp. Để biết thêm thông tin về cách sử dụng các driver được Ably cung cấp, vui lòng tham khảo tài liệu về broadcaster Laravel của Ably.

Laravel Echo là một thư viện JavaScript giúp bạn dễ dàng đăng ký channel và lắng nghe các event do các driver broadcasting server-side của bạn.

Khi cài đặt hỗ trợ broadcasting thông qua lệnh Artisan install:broadcasting --ably, các khung và cấu hình của Ably và Echo sẽ được tự động thêm vào ứng dụng của bạn. Tuy nhiên, nếu bạn muốn tự cấu hình Laravel Echo, bạn có thể thực hiện theo các hướng dẫn bên dưới.

Manual Installation

Để tự cấu hình Laravel Echo cho frontend của ứng dụng, trước tiên hãy cài đặt các package laravel-echopusher-js sử dụng giao thức Pusher cho các subscription, channel và tin nhắn thông qua WebSocket:

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

Trước khi tiếp tục, bạn nên bật hỗ trợ giao thức Pusher trong phần cài đặt ứng dụng Ably của bạn. Bạn có thể bật tính năng này trong phần "Protocol Adapter Settings" của dashboard cài đặt ứng dụng Ably.

Sau khi cài đặt Echo, bạn đã sẵn sàng tạo một instance Echo mới trong file resources/js/bootstrap.js của ứng dụng:

import Echo from 'laravel-echo';

import Pusher from 'pusher-js';
window.Pusher = Pusher;

window.Echo = new Echo({
    broadcaster: 'pusher',
    key: import.meta.env.VITE_ABLY_PUBLIC_KEY,
    wsHost: 'realtime-pusher.ably.io',
    wsPort: 443,
    disableStats: true,
    encrypted: true,
});
import { configureEcho } from "@laravel/echo-react";

configureEcho({
    broadcaster: "ably",
    // key: import.meta.env.VITE_ABLY_PUBLIC_KEY,
    // wsHost: "realtime-pusher.ably.io",
    // wsPort: 443,
    // disableStats: true,
    // encrypted: true,
});
import { configureEcho } from "@laravel/echo-vue";

configureEcho({
    broadcaster: "ably",
    // key: import.meta.env.VITE_ABLY_PUBLIC_KEY,
    // wsHost: "realtime-pusher.ably.io",
    // wsPort: 443,
    // disableStats: true,
    // encrypted: true,
});
import { configureEcho } from "@laravel/echo-svelte";

configureEcho({
    broadcaster: "ably",
    // key: import.meta.env.VITE_ABLY_PUBLIC_KEY,
    // wsHost: "realtime-pusher.ably.io",
    // wsPort: 443,
    // disableStats: true,
    // encrypted: true,
});

Bạn có thể đã nhận thấy cấu hình Ably Echo của chúng ta đang tham chiếu đến biến môi trường VITE_ABLY_PUBLIC_KEY. Giá trị của biến này phải là khóa công khai Ably của bạn. Khóa công khai của bạn là một phần của khóa Ably xuất hiện trước ký tự :.

Khi bạn đã điều chỉnh cấu hình Echo theo nhu cầu của bạn, bạn có thể biên dịch các asset của ứng dụng:

npm run dev

[!NOTE] Để tìm hiểu thêm về cách biên dịch asset JavaScript cho ứng dụng của bạn, vui lòng tham khảo tài liệu về Vite.

Khái niệm tổng quan

Broadcasting event của Laravel cho phép bạn broadcast các event Laravel ở phía máy chủ của bạn tới các application ở JavaScript bên phía client bằng cách sử dụng các phương pháp tiếp cận dựa trên các driver WebSockets. Hiện tại, Laravel hỗ trợ Laravel Reverb, Pusher Channels, và driver Ably. Các event có thể được sử dụng dễ dàng ở phía client bằng cách sử dụng package Javascript Laravel Echo.

Các event được broadcast qua các "channels", có thể chỉ định là công khai hoặc là riêng tư. Bất kỳ client nào truy cập vào application của bạn đều có thể đăng ký channel công khai mà không cần bất kỳ authentication hoặc authorization nào; tuy nhiên, để đăng ký channel private, người dùng phải được authentication và authorization để listen trên channel đó.

Sử dụng một application mẫu

Trước khi đi sâu vào từng thành phần của event broadcasting, bạn có thể có cái nhìn tổng quan bằng cách sử dụng một cửa hàng thương mại điện tử làm ví dụ mẫu.

Trong application của chúng ta, giả sử chúng ta có một trang cho phép người dùng xem trạng thái giao hàng của đơn hàng của họ. Chúng ta cũng giả sử rằng một event OrderShipmentStatusUpdated sẽ được kích hoạt khi một shipping được cập nhật trạng thái bởi application:

use App\Events\OrderShipmentStatusUpdated;

OrderShipmentStatusUpdated::dispatch($order);

The ShouldBroadcast Interface

Khi người dùng đang xem một trong các đơn hàng của họ, chúng ta không muốn họ phải refresh trang để xem lại trạng thái của đơn hàng đó. Thay vào đó, chúng ta muốn broadcast các cập nhật trạng thái cho application của chúng ta khi chúng được tạo. Vì thế, chúng ta cần đánh dấu event OrderShipmentStatusUpdated bằng interface ShouldBroadcast. Điều này sẽ hướng dẫn Laravel là tạo một broadcast event khi event đó được kích hoạt:

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

Interface ShouldBroadcast yêu cầu event của chúng ta cần định nghĩa một phương thức là broadcastOn. Phương thức này sẽ chịu trách nhiệm trả về các channel mà event này sẽ broadcast trên đó. Một empty stub của phương thức này sẽ được định nghĩa sẵn cho chúng ta trên các class event đã được tạo ra, vì vậy chúng ta sẽ chỉ cần điền các thông tin chi tiết về nó. Chúng ta muốn chỉ duy nhất người đã tạo đơn hàng này mới có thể xem trạng thái, vì vậy chúng ta sẽ cần broadcast event này trên một channel private được gắn với đơn đặt hàng:

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

Nếu bạn muốn event được broadcast đến nhiều channel, bạn có thể trả về một 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),
        // ...
    ];
}

Authorizing Channels

Hãy nhớ rằng, người dùng phải có phép thì mới có thể listen trên các channel private. Chúng ta có thể định nghĩa các quy tắc authorization cho channel này trong file routes/channels.php của application. Trong ví dụ này, chúng ta cần kiểm tra rằng bất kỳ người dùng nào đang cố gắng listen trên channel private orders.1 này có phải là người đã tạo ra đơn đặt hàng hay không:

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

Phương thức channel này chấp nhận hai tham số: một là tên của channel và một là callback trả về true hoặc false cho biết người dùng đó có được phép listen trên channel hay không.

Tất cả các authorization callback này đều nhận vào tham số đầu tiên là người dùng hiện tại đang được authenticate và tham số tiếp theo là tham số đại diện cho biến được truyền theo tên của channel. Trong ví dụ này, chúng ta đang sử dụng biến {orderId} để lấy ra phần "ID" trong tên của channel.

Listening For Event Broadcasts

Tiếp theo, tất cả những gì còn lại là listen event trong JavaScript của chúng ta. Chúng ta có thể làm điều này bằng cách sử dụng Laravel Echo. Các hook React, Vue và Svelte được tích hợp sẵn của Laravel Echo giúp việc bắt đầu trở nên dễ dàng, và, mặc định, tất cả các thuộc tính công khai của event sẽ được đưa vào trong broadcast event:

import { useEcho } from "@laravel/echo-react";

useEcho(
    `orders.${orderId}`,
    "OrderShipmentStatusUpdated",
    (e) => {
        console.log(e.order);
    },
);
<script setup lang="ts">
import { useEcho } from "@laravel/echo-vue";

useEcho(
    `orders.${orderId}`,
    "OrderShipmentStatusUpdated",
    (e) => {
        console.log(e.order);
    },
);
</script>
<script>
import { useEcho } from "@laravel/echo-svelte";

useEcho(
    `orders.${orderId}`,
    "OrderShipmentStatusUpdated",
    (e) => {
        console.log(e.order);
    },
);
</script>

Định nghĩa Broadcast Event

Để thông báo cho Laravel rằng một event sẽ được broadcast, bạn phải implement interface Illuminate\Contracts\Broadcasting\ShouldBroadcast trong class event. Mặc định, interface này sẽ được import vào tất cả các class event mà được tạo bởi framework để bạn có thể dễ dàng thêm nó vào bất kỳ event nào của bạn.

Interface ShouldBroadcast yêu cầu bạn implement một phương thức: broadcastOn. Phương thức broadcastOn sẽ trả về tên một channel hoặc một mảng tên các channel mà event sẽ được broadcast trên đó. Các channel phải là các instance của Channel, PrivateChannel, hoặc PresenceChannel. Các instance của Channel là đại diện cho các channel public mà bất kỳ người dùng nào cũng có thể vào, trong khi PrivateChannelsPresenceChannels là đại diện cho các channel private yêu cầu channel authorization:

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

Sau khi implement interface ShouldBroadcast, bạn chỉ cần kích hoạt event như bình thường. Khi event đó đã được kích hoạt, queued job sẽ tự động broadcast event đó thông qua driver broadcast mà chúng ta đã định nghĩa.

Broadcast Name

Mặc định, Laravel sẽ broadcast event bằng tên class của event. Tuy nhiên, bạn có thể tùy chỉnh tên của broadcast bằng cách định nghĩa phương thức broadcastAs trong event:

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

Nếu bạn tùy chỉnh tên broadcast bằng phương thức broadcastAs, bạn nên đảm bảo rằng đã đăng ký listener của bạn với một ký tự . ở đầu. Điều này sẽ hướng dẫn Echo không thêm namespace application cho event của bạn:

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

Broadcast Data

Khi một event đã được broadcast, thì tất cả các thuộc tính public của nó sẽ tự động serialize và broadcast dưới dạng payload của một event, cho phép bạn truy cập bất kỳ dữ liệu công khai nào từ JavaScript của bạn. Vì thế, ví dụ, nếu event của bạn có một thuộc tính $user công khai là một model Eloquent, thì payload của broadcast event sẽ là:

{
    "user": {
        "id": 1,
        "name": "Patrick Stewart"
        ...
    }
}

Tuy nhiên, nếu bạn muốn có quyền kiểm soát chi tiết hơn đối với payload broadcast của bạn, bạn có thể thêm một phương thức broadcastWith vào event của bạn. Phương thức này sẽ trả về mảng dữ liệu mà bạn muốn broadcast dưới dạng payload event:

/**
 * Get the data to broadcast.
 *
 * @return array<string, mixed>
 */
public function broadcastWith(): array
{
    return ['id' => $this->user->id];
}

Broadcast Queue

Mặc định, mỗi broadcast event sẽ được đặt trên một queue mặc định với một kết nối queue mặc định được định nghĩa trong file cấu hình queue.php của bạn. Bạn có thể tùy chỉnh kết nối queue và tên được broadcaster sử dụng bằng cách sử dụng các thuộc tính ConnectionQueue trên class event của bạn:

use Illuminate\Queue\Attributes\Connection;
use Illuminate\Queue\Attributes\Queue;

#[Connection('redis')]
#[Queue('default')]
class ServerCreated implements ShouldBroadcast
{
    // ...
}

Ngoài ra, bạn có thể tùy chỉnh tên queue bằng cách định nghĩa phương thức broadcastQueue cho event của bạn:

/**
 * The name of the queue on which to place the broadcasting job.
 */
public function broadcastQueue(): string
{
    return 'default';
}

Nếu bạn muốn broadcast event của bạn bằng queue sync thay vì driver queue mặc định, bạn có thể implement interface ShouldBroadcastNow thay vì ShouldBroadcast:

<?php

namespace App\Events;

use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;

class OrderShipmentStatusUpdated implements ShouldBroadcastNow
{
    // ...
}

Broadcast Conditions

Thỉnh thoảng bạn cũng có thể muốn broadcast event của bạn trong một điều kiện nhất định. Bạn có thể định nghĩa các điều kiện này bằng cách thêm một phương thức broadcastWhen vào trong class event của bạn:

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

Broadcasting và Database Transactions

Khi các broadcast event được gửi đi trong các database transaction, chúng có thể được queue xử lý trước khi database transaction được thực hiện. Khi điều này xảy ra, bất kỳ cập nhật nào mà bạn đã thực hiện đối với model hoặc record cơ sở dữ liệu trong quá trình database transaction có thể chưa được phản ánh trong cơ sở dữ liệu. Ngoài ra, bất kỳ model hoặc record cơ sở dữ liệu nào được tạo trong transaction có thể không tồn tại trong cơ sở dữ liệu. Nếu event của bạn phụ thuộc vào các model này, lỗi không mong muốn có thể xảy ra khi job broadcast event được xử lý.

Nếu tùy chọn cấu hình after_commit của queue connection của bạn được set thành false, thì bạn vẫn có thể cho biết một broadcast event sẽ được gửi đi sau khi tất cả các database transaction đã được thực hiện bằng cách implement interface ShouldDispatchAfterCommit trên class event đó:

<?php

namespace App\Events;

use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Contracts\Events\ShouldDispatchAfterCommit;
use Illuminate\Queue\SerializesModels;

class ServerCreated implements ShouldBroadcast, ShouldDispatchAfterCommit
{
    use SerializesModels;
}

[!NOTE] Để tìm hiểu thêm về cách khắc phục những sự cố như thế này, vui lòng xem lại tài liệu về queued job và database transaction.

Authorizing Channels

Các channel private sẽ yêu cầu bạn authorize rằng người dùng hiện tại đang được authenticate có thể có listen trên channel private này hay không. Điều này có thể được thực hiện bằng cách tạo một HTTP request đến application Laravel của bạn với tên channel và sau đó application của bạn có thể xác định xem người dùng đó có thể listen trên channel đó hay không. Khi sử dụng Laravel Echo, thì HTTP request authorize này sẽ được tạo ra tự động.

Khi broadcast được cài đặt xong , Laravel sẽ thử tự động đăng ký route /broadcasting/auth để xử lý các request xác thực. Nếu Laravel không tự động đăng ký các route này, bạn có thể tự đăng ký chúng trong file /bootstrap/app.php của ứng dụng:

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

Định nghĩa Authorization Callback

Tiếp theo, chúng ta cần định nghĩa các logic sẽ được determine if the currently authenticated user can listen to a given channel. Điều này sẽ được thực hiện trong file routes/channels.php được tạo ra bởi lệnh Artisan install:broadcasting. Trong file này, bạn có thể sử dụng phương thức Broadcast::channel để đăng ký các callback authorization channel:

use App\Models\User;

Broadcast::channel('orders.{orderId}', function (User $user, int $orderId) {
    return $user->id === Order::findOrNew($orderId)->user_id;
});

Phương thức channel chấp nhận hai tham số: một là tên của channel và một là callback trả về true hoặc false cho biết người dùng đó có được phép listen trên channel này hay không.

Tất cả các callback authorization đều nhận vào tham số đầu tiên là người dùng hiện tại đang được authenticate và tham số tiếp theo là một tham số đại diện. Trong ví dụ này, chúng ta đang sử dụng biến {orderId} để chỉ ra phần "ID" của tên channel.

Bạn có thể xem một danh sách các callback của broadcast authorization trong ứng dụng của bằng lệnh Artisan channel:list:

php artisan channel:list

Authorization Callback Model Binding

Giống như các route HTTP, các route channel cũng có thể tận dụng các route model binding. Ví dụ, thay vì nhận một chuỗi ID hoặc một chuỗi số thứ tự, bạn có thể yêu cầu một instance model Order:

use App\Models\Order;
use App\Models\User;

Broadcast::channel('orders.{order}', function (User $user, Order $order) {
    return $user->id === $order->user_id;
});

[!WARNING] Không giống như liên kết model route HTTP, liên kết model channel không cung cấp hỗ trợ tự động scope theo liên kết model ngầm. Tuy nhiên, đây hiếm khi là vấn đề vì hầu hết các channel có thể được xác định scope dựa trên khóa chính, duy nhất của một model.

Authorization Callback Authentication

Các channel broadcast private và presence sẽ xác thực người dùng hiện tại thông qua authentication guard mặc định của ứng dụng. Nếu người dùng không được xác thực, channel authorization cũng sẽ tự động bị từ chối và lệnh authorization callback cũng sẽ không bao giờ được thực thi. Tuy nhiên, bạn có thể chỉ định nhiều guard tùy chỉnh khác sẽ xác thực request đến nếu cần:

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

Định nghĩa Channel Class

Nếu ứng dụng của bạn sử dụng nhiều channel khác nhau, thì file routes/channels.php của bạn có thể trở nên rất cồng kềnh. Vì vậy, thay vì sử dụng closure để cấp quyền cho các channel, bạn có thể sử dụng các class channel. Để tạo một class channel mới, hãy sử dụng lệnh Artisan make:channel. Lệnh này sẽ lưu một class channel mới vào trong thư mục App/Broadcasting.

php artisan make:channel OrderChannel

Tiếp theo, đăng ký channel của bạn vào trong file routes/channels.php:

use App\Broadcasting\OrderChannel;

Broadcast::channel('orders.{order}', OrderChannel::class);

Cuối cùng, bạn có thể viết các logic cấp quyền cho channel của bạn vào trong phương thức join của class channel. Phương thức join sẽ chứa cùng một logic với code mà bạn thường viết trong closure cấp quyền channel của bạn. Bạn cũng có thể tận dụng lợi thế của liên kết model channel:

<?php

namespace App\Broadcasting;

use App\Models\Order;
use App\Models\User;

class OrderChannel
{
    /**
     * Create a new channel instance.
     */
    public function __construct() {}

    /**
     * Authenticate the user's access to the channel.
     */
    public function join(User $user, Order $order): array|bool
    {
        return $user->id === $order->user_id;
    }
}

[!NOTE] Giống như nhiều class khác trong Laravel, các class channel sẽ tự động được resolve bởi service container. Vì vậy, bạn có thể khai báo bất kỳ phụ thuộc nào mà channel của bạn cần trong hàm tạo của nó.

Broadcasting Event

Khi bạn đã định nghĩa một event và đánh dấu nó bằng một interface ShouldBroadcast, bạn chỉ cần kích hoạt event đó bằng hàm dispatch của event. Dispatcher của event sẽ hiểu được rằng event đó đã được đánh dấu bằng một interface ShouldBroadcast nên nó sẽ tạo một queue event để broadcasting:

use App\Events\OrderShipmentStatusUpdated;

OrderShipmentStatusUpdated::dispatch($order);

Only To Others

Khi xây dựng một application sử dụng event broadcasting, đôi khi bạn có thể cần broadcast một event cho tất cả những người đăng ký trên một channel nhất định ngoại trừ người dùng hiện tại. Bạn có thể thực hiện việc này bằng cách sử dụng helper broadcast và phương thức toOthers:

use App\Events\OrderShipmentStatusUpdated;

broadcast(new OrderShipmentStatusUpdated($update))->toOthers();

Để hiểu rõ hơn lý do mà bạn có thể muốn sử dụng phương thức toOthers, hãy tưởng tượng một application quản lý danh sách các task trong đó người dùng có thể tạo ra một task mới bằng cách nhập tên task. Để tạo một task mới, application của bạn có thể tạo một request đến url /task để broadcasts tạo task và trả về một JSON là một task mới. Khi JavaScript của bạn nhận được phản hồi từ route, nó có thể trực tiếp chèn task mới này vào danh sách các task đã tồn tại như sau:

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

Tuy nhiên, hãy nhớ rằng chúng ta đang broadcast một event tạo task. Nếu JavaScript của bạn cũng đang listening event này, để thêm task mới vào danh sách task, thì bạn có thể có các task trùng lặp trong danh sách của bạn: một là từ route và một là từ broadcast. Bạn có thể giải quyết điều này bằng cách sử dụng phương thức toOthers để hướng dẫn broadcaster không broadcast event tới người dùng hiện tại.

[!WARNING] Event của bạn phải sử dụng trait Illuminate\Broadcasting\InteractsWithSockets để gọi phương thức toOthers.

Cấu hình

Khi bạn khởi tạo một instance Laravel Echo, một ID socket cũng sẽ được khởi tạo. Nếu bạn đang sử dụng một global instance Axios để thực hiện các request HTTP từ ứng dụng JavaScript của bạn, thì ID socket đó sẽ được tự động đính kèm vào mọi request gửi đi dưới dạng một X-Socket-ID header. Sau đó, khi bạn gọi phương thức toOthers, Laravel sẽ lấy ID socket từ header và hướng dẫn broadcaster sẽ không broadcast đến bất kỳ kết nối nào mà trùng với ID socket đó.

Nếu bạn không sử dụng một global Axios instance, bạn sẽ cần phải tự cấu hình JavaScript của bạn để gửi header X-Socket-ID với tất cả các request gửi đi. Bạn có thể lấy ra ID socket bằng phương thức Echo.socketId:

var socketId = Echo.socketId();

Tùy chỉnh Connection

Nếu ứng dụng của bạn tương tác với nhiều kết nối broadcast và bạn muốn broadcast một event bằng cách sử dụng một broadcaster khác, khác với mặc định của bạn, thì bạn có thể chỉ định kết nối đó bằng cách sử dụng phương thức via:

use App\Events\OrderShipmentStatusUpdated;

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

Ngoài ra, bạn có thể chỉ định kết nối broadcast của event bằng cách gọi phương thức broadcastVia trong hàm khởi tạo của event. Tuy nhiên, trước khi làm như vậy, bạn nên đảm bảo rằng class event đã sử dụng trait 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');
    }
}

Event ẩn

Thỉnh thoảng, bạn có thể muốn broadcast một event đơn giản đến frontend của người dùng mà không cần thiết phải tạo một class event chuyên dụng. Để đáp ứng điều này, facade Broadcast cho phép bạn broadcast một "event ẩn":

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

Ví dụ trên sẽ broadcast ra một event sau:

{
    "event": "AnonymousEvent",
    "data": "[]",
    "channel": "orders.1"
}

Sử dụng phương thức aswith, bạn có thể tùy chỉnh tên và dữ liệu của event:

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

Ví dụ trên sẽ broadcast ra một event như sau:

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

Nếu bạn muốn broadcast ra event ẩn trên một channel riêng tư hoặc một channel presence, thì bạn có thể sử dụng phương thức privatepresence:

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

Broadcast một event ẩn bằng phương thức send sẽ gửi một event đến queue của ứng dụng để xử lý. Tuy nhiên, nếu bạn muốn broadcast event ngay mà không cần queue, bạn có thể sử dụng phương thức sendNow:

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

Để broadcast một event tới tất cả người đăng ký channel ngoại trừ người dùng hiện tại, bạn có thể gọi phương thức toOthers:

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

Xử lý lỗi khi broadcast

Khi server queue của ứng dụng không sẵn sàng hoặc Laravel gặp lỗi trong khi broadcast một event, một exception sẽ được đưa ra và thường khiến người dùng thấy lỗi của ứng dụng. Do việc broadcast event thường chỉ là phần bổ trợ cho chức năng cốt lõi của ứng dụng, bạn có thể ngăn các exception này làm gián đoạn trải nghiệm người dùng bằng cách implement một interface ShouldRescue trên các event của bạn.

Các event mà implement interface ShouldRescue sẽ được tự động sử dụng helper function rescue của Laravel trong quá trình broadcast. Helper này sẽ catch mọi exception, report chúng cho exception handler của ứng dụng để ghi log, và cho phép ứng dụng tiếp tục thực thi bình thường mà không làm gián đoạn luồng công việc của người dùng:

<?php

namespace App\Events;

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

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

Receiving Broadcasts

Listening cho Event

Khi bạn đã cài đặt và khởi tạo Laravel Echo xong, bạn đã sẵn sàng để bắt đầu listening các event được broadcast từ ứng dụng Laravel của bạn. Đầu tiên, hãy sử dụng phương thức channel để lấy ra một instance của một channel, sau đó gọi phương thức listen để listen một event cụ thể:

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

Nếu bạn muốn listen các event trên một private channel, hãy sử dụng phương thức private. Bạn có thể khai báo nhiều phương thức listen để listen nhiều event trên một channel:

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

Stop Listening For Events

Nếu bạn muốn dừng listening một event mà không phải rời khỏi channel, bạn có thể sử dụng phương thức stopListening:

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

Rời một Channel

Để rời khỏi một channel, bạn có thể gọi phương thức leaveChannel trên instance Echo của bạn:

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

Nếu bạn muốn rời khỏi một channel cũng như các channel riêng tư và presence channel khác, bạn có thể gọi phương thức leave:

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

Namespaces

Bạn có thể đã nhận thấy trong các ví dụ ở trên, chúng ta không chỉ định namespace App\Events cho các class event. Điều này là do Echo đã tự động giả định các event được lưu trong namespace App\Events. Tuy nhiên, bạn có thể cấu hình namespace khi bạn khởi tạo Echo bằng cách truyền vào một tùy chọn cấu hình namespace:

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

Ngoài ra, bạn có thể thêm tiền tố cho các class event với một dấu . khi theo dõi các event bằng Echo. Điều này sẽ cho phép bạn chỉ định tên class:

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

Sử dụng React, Vue hoặc Svelte

Laravel Echo có chứa các hook React, Vue và Svelte giúp việc lắng nghe các event trở nên dễ dàng. Để bắt đầu, hãy gọi hook useEcho, hook này được sử dụng để lắng nghe các event private. Hook useEcho sẽ tự động thoát khỏi các channel khi component sử dụng nó bị unmount:

import { useEcho } from "@laravel/echo-react";

useEcho(
    `orders.${orderId}`,
    "OrderShipmentStatusUpdated",
    (e) => {
        console.log(e.order);
    },
);
<script setup lang="ts">
import { useEcho } from "@laravel/echo-vue";

useEcho(
    `orders.${orderId}`,
    "OrderShipmentStatusUpdated",
    (e) => {
        console.log(e.order);
    },
);
</script>
<script>
import { useEcho } from "@laravel/echo-svelte";

useEcho(
    `orders.${orderId}`,
    "OrderShipmentStatusUpdated",
    (e) => {
        console.log(e.order);
    },
);
</script>

Bạn có thể lắng nghe nhiều event bằng cách cung cấp một mảng các event cho useEcho:

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

Bạn cũng có thể chỉ định cấu trúc của dữ liệu payload của event broadcast, giúp tăng tính an toàn về kiểu dữ liệu và thuận tiện hơn khi chỉnh sửa:

type OrderData = {
    order: {
        id: number;
        user: {
            id: number;
            name: string;
        };
        created_at: string;
    };
};

useEcho<OrderData>(`orders.${orderId}`, "OrderShipmentStatusUpdated", (e) => {
    console.log(e.order.id);
    console.log(e.order.user.id);
});

Hook useEcho sẽ tự động thoát khỏi các channel khi component sử dụng nó bị unmount; tuy nhiên, bạn có thể sử dụng các hàm được trả về để stop hoặc start lắng nghe các channel một cách thủ công khi cần thiết:

import { useEcho } from "@laravel/echo-react";

const { leaveChannel, leave, stopListening, listen } = useEcho(
    `orders.${orderId}`,
    "OrderShipmentStatusUpdated",
    (e) => {
        console.log(e.order);
    },
);

// Stop listening without leaving channel...
stopListening();

// Start listening again...
listen();

// Leave channel...
leaveChannel();

// Leave a channel and also its associated private and presence channels...
leave();
<script setup lang="ts">
import { useEcho } from "@laravel/echo-vue";

const { leaveChannel, leave, stopListening, listen } = useEcho(
    `orders.${orderId}`,
    "OrderShipmentStatusUpdated",
    (e) => {
        console.log(e.order);
    },
);

// Stop listening without leaving channel...
stopListening();

// Start listening again...
listen();

// Leave channel...
leaveChannel();

// Leave a channel and also its associated private and presence channels...
leave();
</script>
<script>
import { useEcho } from "@laravel/echo-svelte";

const { leaveChannel, leave, stopListening, listen } = useEcho(
    `orders.${orderId}`,
    "OrderShipmentStatusUpdated",
    (e) => {
        console.log(e.order);
    },
);

// Stop listening without leaving channel...
stopListening();

// Start listening again...
listen();

// Leave channel...
leaveChannel();

// Leave a channel and also its associated private and presence channels...
leave();
</script>

Connecting to Public Channels

Để kết nối với một public channel, bạn có thể sử dụng hook useEchoPublic:

import { useEchoPublic } from "@laravel/echo-react";

useEchoPublic("posts", "PostPublished", (e) => {
    console.log(e.post);
});
<script setup lang="ts">
import { useEchoPublic } from "@laravel/echo-vue";

useEchoPublic("posts", "PostPublished", (e) => {
    console.log(e.post);
});
</script>
<script>
import { useEchoPublic } from "@laravel/echo-svelte";

useEchoPublic("posts", "PostPublished", (e) => {
    console.log(e.post);
});
</script>

Connecting to Presence Channels

Để kết nối với một presence channel, bạn có thể sử dụng hook useEchoPresence:

import { useEchoPresence } from "@laravel/echo-react";

useEchoPresence("posts", "PostPublished", (e) => {
    console.log(e.post);
});
<script setup lang="ts">
import { useEchoPresence } from "@laravel/echo-vue";

useEchoPresence("posts", "PostPublished", (e) => {
    console.log(e.post);
});
</script>
<script>
import { useEchoPresence } from "@laravel/echo-svelte";

useEchoPresence("posts", "PostPublished", (e) => {
    console.log(e.post);
});
</script>

Connection Status

Bạn có thể lấy trạng thái kết nối của WebSocket hiện tại bằng cách sử dụng hook useConnectionStatus, nó sẽ tự động cập nhật trạng thái khi trạng thái kết nối thay đổi:

import { useConnectionStatus } from "@laravel/echo-react";

function ConnectionIndicator() {
    const status = useConnectionStatus();

    return <div>Connection: {status}</div>;
}
<script setup lang="ts">
import { useConnectionStatus } from "@laravel/echo-vue";

const status = useConnectionStatus();
</script>

<template>
    <div>Connection: {{ status }}</div>
</template>
<script>
import { useConnectionStatus } from "@laravel/echo-svelte";

const status = useConnectionStatus();
</script>

<div>Connection: {status()}</div>

Các giá trị trạng thái có thể có là:

  • connected - Đã kết nối thành công với server WebSocket.
  • connecting - Đang kết nối.
  • reconnecting - Đang kết nối lại sau khi bị ngắt kết nối.
  • disconnected - Không được kết nối.
  • failed - Kết nối thất bại.

Presence Channel

Các presence channel được xây dựng dựa trên tính bảo mật của các private channel đồng thời thêm các chức năng về những người đã theo dõi channel. Điều này giúp dễ dàng xây dựng các tính năng mạnh mẽ cho application chẳng hạn như thông báo cho người dùng biết khi một người dùng khác đang xem cùng một trang hoặc liệt kê những người đang trong một phòng trò chuyện.

Authorizing Presence Channel

Tất cả các presence channel cũng là các private channel; do đó, người dùng phải được authorized để truy cập đến nó. Tuy nhiên, khi định nghĩa các authorization callback cho các presence channel, bạn sẽ không trả về true nếu người dùng đã được authorize tham gia channel. Thay vào đó, bạn nên trả về một mảng dữ liệu về người dùng đó.

Dữ liệu được trả về bởi authorization callback cũng sẽ được cung cấp cho những người khác đang listen event trong presence channel của JavaScript của bạn. Nếu người dùng không được phép tham gia presence channel, bạn nên trả về false hoặc 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];
    }
});

Tham gia Presence Channel

Để tham gia một presence channel, bạn có thể sử dụng phương thức join của Echo. Phương thức join sẽ trả về một implementation PresenceChannel, cùng với việc thêm phương thức listen, cho phép bạn theo dõi các event here, joining, và leaving.

Echo.join(`chat.${roomId}`)
    .here((users) => {
        // ...
    })
    .joining((user) => {
        console.log(user.name);
    })
    .leaving((user) => {
        console.log(user.name);
    })
    .error((error) => {
        console.error(error);
    });

Callback here sẽ được thực hiện ngay sau khi kết nối thành công đến channel và sẽ nhận về một mảng chứa thông tin của tất cả các người dùng đang đăng ký channel. Phương thức joining sẽ được thực thi khi một người dùng mới tham gia vào channel, trong khi phương thức leaving sẽ được thực thi khi một người dùng rời khỏi channel. Phương thức error sẽ được thực thi khi xác thực trả về một HTTP status khác 200 hoặc nếu có sự cố khi phân tích cú pháp JSON được trả về.

Broadcasting tới Presence Channel

Các presence channel có thể nhận các event giống như các public hoặc private channel. Ví dụ như về một chatroom, chúng ta có thể muốn broadcast các event NewMessage lên một room. Để làm như vậy, chúng ta sẽ trả về một instance của PresenceChannel từ phương thức broadcastOn của event:

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

Cũng như các event khác, bạn có thể sử dụng helper broadcast và phương thức toOthers để loại người dùng hiện tại ra khỏi việc nhận broadcast:

broadcast(new NewMessage($message));

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

Là điển hình của các loại event khác, bạn có thể listen các event được gửi đến các presence channel bằng phương thức listen của Echo:

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

Model Broadcasting

[!WARNING] Trước khi đọc tài liệu dưới đây về model broadcasting, chúng tôi khuyên bạn nên làm quen với các khái niệm chung về các service model broadcasting của Laravel cũng như cách tạo và lắng nghe các broadcast event.

Thông thường sẽ broadcast các event khi model Eloquent của ứng dụng của bạn được tạo, cập nhật hoặc xóa. Tất nhiên, điều này có thể dễ dàng được thực hiện bằng cách định nghĩa các custom event cho các thay đổi trạng thái của model Eloquent và đánh dấu các event đó bằng interface ShouldBroadcast.

Tuy nhiên, nếu bạn không sử dụng các event này cho bất kỳ mục đích nào khác trong ứng dụng của bạn, thì việc tạo các class event cho mục đích duy nhất là broadcasting chúng có thể rất khó khăn. Để khắc phục điều này, Laravel cho phép bạn chỉ ra một Eloquent model sẽ tự động broadcast các thay đổi trạng thái của nó.

Để bắt đầu, model Eloquent của bạn nên sử dụng trait Illuminate\Database\Eloquent\BroadcastsEvents. Ngoài ra, model nên định nghĩa một phương thức broadcastOn, phương thức này sẽ trả về một mảng các channel mà các event của model đó sẽ broadcast trên đó:

<?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];
    }
}

Khi model của bạn đã chứa trait này và định nghĩa các channel broadcast nó, nó sẽ bắt đầu tự động broadcast các event khi một instance model được tạo, cập nhật, xóa, soft delete hoặc restore.

Ngoài ra, bạn có thể nhận thấy rằng phương thức broadcastOn có nhận được một tham số $event là string. Tham số này chứa loại event đã xảy ra trên model và sẽ có giá trị là created, updated, deleted, trashed hoặc restored. Bằng cách kiểm tra giá trị của biến này, bạn có thể xác định xem channel nào (nếu có) mà model sẽ broadcast với một event cụ thể:

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

Customizing Model Broadcasting Event Creation

Thỉnh thoảng, bạn có thể muốn tùy chỉnh cách Laravel tạo event model broadcasting. Bạn có thể thực hiện việc này bằng cách định nghĩa phương thức newBroadcastableEvent trên model Eloquent của bạn. Phương thức này sẽ trả về một instance Illuminate\Database\Eloquent\BroadcastableModelEventOccurred:

use Illuminate\Database\Eloquent\BroadcastableModelEventOccurred;

/**
 * Create a new broadcastable model event for the model.
 */
protected function newBroadcastableEvent(string $event): BroadcastableModelEventOccurred
{
    return (new BroadcastableModelEventOccurred(
        $this, $event
    ))->dontBroadcastToCurrentUser();
}

Model Broadcasting Conventions

Channel Conventions

Như bạn có thể nhận thấy, phương thức broadcastOn trong ví dụ model ở trên không trả về các instance Channel. Thay vào đó, các model Eloquent được trả về trực tiếp. Nếu một instance model Eloquent được trả về trực tiếp bởi phương thức broadcastOn của model của bạn (hoặc được chứa trong một mảng được phương thức này trả về), Laravel sẽ tự động khởi tạo một instance private channel cho model đó bằng cách sử dụng tên class của model và identifier khóa chính của model làm tên channel.

Vì vậy, model App\Models\Userid1 sẽ được chuyển thành một instance Illuminate\Broadcasting\PrivateChannel với tên là App.Models.User.1. Tất nhiên, ngoài việc trả về các instance model Eloquent từ phương thức broadcastOn của model, bạn có thể trả về các instance Channel hoàn chỉnh để có toàn quyền kiểm soát tên channel của model:

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

Nếu bạn định trả về một instance channel từ phương thức broadcastOn của model, thì bạn có thể truyền một instance model Eloquent cho hàm khởi tạo của channel. Khi làm như vậy, Laravel sẽ sử dụng các quy ước về model channel đã thảo luận ở trên để chuyển đổi model Eloquent thành chuỗi tên channel:

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

Nếu bạn cần xác định xem tên channel của một model, bạn có thể gọi phương thức broadcastChannel trên bất kỳ instance model nào. Ví dụ: phương thức này trả về chuỗi App.Models.User.1 cho một model App\Models\User với id1:

$user->broadcastChannel();

Event Conventions

Vì các event model broadcast không được liên kết với một event "thực tế" trong thư mục App\Events của ứng dụng của bạn, nên chúng được gán một tên và payload dựa trên các quy ước. Quy ước của Laravel là broadcast event bằng cách sử dụng tên class của model (không bao gồm namespace) và tên của event model đã kích hoạt broadcast.

Vì vậy, ví dụ: cập nhật của model App\Models\Post sẽ broadcast ra một event tới ứng dụng phía client của bạn dưới dạng là PostUpdated với payload như sau:

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

Việc xóa một model App\Models\User sẽ broadcast ra một event có tên là UserDeleted.

Nếu muốn, bạn có thể định nghĩa một tùy biến tên broadcast và payload của nó bằng cách thêm hai phương thức broadcastAsbroadcastWith vào model của bạn. Các phương thức này nhận vào tên của event và hoạt động model đang diễn ra, cho phép bạn tùy biến tên và payload của event cho từng hoạt động của model. Nếu null được trả về từ phương thức broadcastAs, Laravel sẽ sử dụng các quy ước tên event model broadcast đã thảo luận ở trên khi broadcasting event:

/**
 * The model event's broadcast name.
 */
public function broadcastAs(string $event): string|null
{
    return match ($event) {
        'created' => 'post.created',
        default => null,
    };
}

/**
 * Get the data to broadcast for the model.
 *
 * @return array<string, mixed>
 */
public function broadcastWith(string $event): array
{
    return match ($event) {
        'created' => ['title' => $this->title],
        default => ['model' => $this],
    };
}

Listening For Model Broadcasts

Khi bạn đã thêm trait BroadcastsEvents vào model của bạn và định nghĩa phương thức broadcastOn của model, bạn đã sẵn sàng bắt đầu lắng nghe các event model broadcast trong ứng dụng bên phía client của bạn. Trước khi bắt đầu, bạn có thể muốn tham khảo tài liệu về lắng nghe event.

Trước tiên, hãy sử dụng phương thức private để lấy ra một instance của channel, sau đó gọi phương thức listen để lắng nghe một event cụ thể. Thông thường, tên channel sẽ được set cho phương thức private phải tương ứng với quy ước model broadcast của Laravel.

Sau khi bạn đã nhận được instance channel, bạn có thể sử dụng phương thức listen để lắng nghe một event cụ thể. Vì các event model broadcast không được liên kết với một event "thực tế" nào trong thư mục App\Events của ứng dụng của bạn, nên tên event phải có tiền tố là . để biểu thị nó không thuộc về một namespace cụ thể nào. Mỗi event model broadcast có một thuộc tính model chứa tất cả các thuộc tính có thể broadcast của model:

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

Sử dụng React, Vue, hoặc Svelte

Nếu bạn đang sử dụng React, Vue, hoặc Svelte, bạn có thể sử dụng hook useEchoModel có sẵn của Laravel Echo để dễ dàng lắng nghe các model broadcast:

import { useEchoModel } from "@laravel/echo-react";

useEchoModel("App.Models.User", userId, ["UserUpdated"], (e) => {
    console.log(e.model);
});
<script setup lang="ts">
import { useEchoModel } from "@laravel/echo-vue";

useEchoModel("App.Models.User", userId, ["UserUpdated"], (e) => {
    console.log(e.model);
});
</script>
<script>
import { useEchoModel } from "@laravel/echo-svelte";

useEchoModel("App.Models.User", userId, ["UserUpdated"], (e) => {
    console.log(e.model);
});
</script>

Bạn cũng có thể chỉ định cấu trúc của dữ liệu payload của model event, mang lại tính an toàn kiểu dữ liệu cao hơn và sự thuận tiện khi chỉnh sửa:

type User = {
    id: number;
    name: string;
    email: string;
};

useEchoModel<User, "App.Models.User">("App.Models.User", userId, ["UserUpdated"], (e) => {
    console.log(e.model.id);
    console.log(e.model.name);
});

Client Event

[!NOTE] Khi sử dụng Pusher Channels, bạn phải bật tùy chọn "Client Events" trong phần "App Settings" của bảng điều khiển ứng dụng để gửi các client event.

Thỉnh thoảng bạn có thể muốn broadcast một event cho những client được kết nối khác mà không cần gọi application Laravel của bạn. Điều này có thể đặc biệt hữu ích cho những việc như thông báo "đang gõ", bạn muốn thông báo cho người dùng application của bạn rằng có một người dùng khác đang gõ một tin nhắn trên màn hình.

Để broadcast các client event, bạn có thể sử dụng phương thức whisper của Echo:

Echo.private(`chat.${roomId}`)
    .whisper('typing', {
        name: this.user.name
    });
import { useEcho } from "@laravel/echo-react";

const { channel } = useEcho(`chat.${roomId}`, ['update'], (e) => {
    console.log('Chat event received:', e);
});

channel().whisper('typing', { name: user.name });
<script setup lang="ts">
import { useEcho } from "@laravel/echo-vue";

const { channel } = useEcho(`chat.${roomId}`, ['update'], (e) => {
    console.log('Chat event received:', e);
});

channel().whisper('typing', { name: user.name });
</script>
<script>
import { useEcho } from "@laravel/echo-svelte";

const { channel } = useEcho(`chat.${roomId}`, ['update'], (e) => {
    console.log('Chat event received:', e);
});

channel().whisper('typing', { name: user.name });
</script>

Để listen các client event, bạn có thể sử dụng phương thức listenForWhisper:

Echo.private(`chat.${roomId}`)
    .listenForWhisper('typing', (e) => {
        console.log(e.name);
    });
import { useEcho } from "@laravel/echo-react";

const { channel } = useEcho(`chat.${roomId}`, ['update'], (e) => {
    console.log('Chat event received:', e);
});

channel().listenForWhisper('typing', (e) => {
    console.log(e.name);
});
<script setup lang="ts">
import { useEcho } from "@laravel/echo-vue";

const { channel } = useEcho(`chat.${roomId}`, ['update'], (e) => {
    console.log('Chat event received:', e);
});

channel().listenForWhisper('typing', (e) => {
    console.log(e.name);
});
</script>
<script>
import { useEcho } from "@laravel/echo-svelte";

const { channel } = useEcho(`chat.${roomId}`, ['update'], (e) => {
    console.log('Chat event received:', e);
});

channel().listenForWhisper('typing', (e) => {
    console.log(e.name);
});
</script>

Notification

Bằng cách kết nối event broadcasting với notifications, JavaScript của bạn có thể nhận được thông báo mới khi chúng xảy ra mà không cần refresh trang. Trước khi bắt đầu, hãy nhớ đọc tài liệu về việc sử dụng channel thông báo broadcast.

Khi bạn đã cấu hình thông báo sử dụng broadcast channel, bạn có thể listen các broadcast event bằng phương thức notification của Echo. Hãy nhớ rằng, tên channel phải khớp với tên class nhận thông báo:

Echo.private(`App.Models.User.${userId}`)
    .notification((notification) => {
        console.log(notification.type);
    });
import { useEchoModel } from "@laravel/echo-react";

const { channel } = useEchoModel('App.Models.User', userId);

channel().notification((notification) => {
    console.log(notification.type);
});
<script setup lang="ts">
import { useEchoModel } from "@laravel/echo-vue";

const { channel } = useEchoModel('App.Models.User', userId);

channel().notification((notification) => {
    console.log(notification.type);
});
</script>
<script>
import { useEchoModel } from "@laravel/echo-svelte";

const { channel } = useEchoModel('App.Models.User', userId);

channel().notification((notification) => {
    console.log(notification.type);
});
</script>

Trong ví dụ trên, tất cả các thông báo được gửi đến instance App\Models\User thông qua channel broadcast sẽ được nhận được thông qua hàm callback. Một callback authorization cho channel App.Models.User.{id} sẽ có sẵn trong BroadcastServiceProvider đi kèm với framework Laravel.

Stop Listening for Notifications

If you would like to stop listening to notifications without leaving the channel, you may use the stopListeningForNotification method:

const callback = (notification) => {
    console.log(notification.type);
}

// Start listening...
Echo.private(`App.Models.User.${userId}`)
    .notification(callback);

// Stop listening (callback must be the same)...
Echo.private(`App.Models.User.${userId}`)
    .stopListeningForNotification(callback);
Artisan Console Cache
© 2023 by Logo page doc-vn