← Tình huống

Laravel Echo mà không cần chạy Reverb

Đổi vài giá trị .env; event, kênh, whisper và toOthers() vẫn hoạt động.

Laravel Broadcasting vốn đã dùng giao thức Pusher, nên một ứng dụng Laravel có thể gửi broadcast tới Hibira bằng kết nối pusher có sẵn. Event, route kênh và mã Echo giữ nguyên. Thứ biến mất là máy chủ WebSocket mà bạn lẽ ra phải tự chạy: một tiến trình Reverb chạy liên tục, Redis để mở rộng ra hơn một máy, cân bằng tải sticky, và một người phải trông chừng nó lúc nửa đêm.

Người dùng nhận được gì

  • Trạng thái đơn hàng, thông báo và bảng điều khiển tự cập nhật mà không cần tải lại.
  • Chat có "đang nhập…" và danh sách trực tuyến, qua join và whisper của Echo.
  • Trạng thái mới nhất ngay khi trang mở, thay vì màn hình trống cho tới event kế tiếp.

Dùng những gì

  • Laravel Broadcasting với kết nối pusher và pusher/pusher-php-server.
  • Laravel Echo với pusher-js trong trình duyệt.
  • Kênh riêng tư và kênh presence, được cấp quyền bằng chính routes/channels.php của bạn.
  • Client event (Echo whisper) và loại trừ người gửi (toOthers()).
  • Kênh cache (cache-), giao event cuối cùng cho mọi người đăng ký mới.

Bước 1: trỏ Broadcasting tới Hibira

Từ Laravel 11 trở đi, cài broadcasting và chọn Pusher khi được hỏi:

php artisan install:broadcasting
composer require pusher/pusher-php-server
npm install --save-dev laravel-echo pusher-js

Sau đó đặt kết nối trong .env. Các giá trị có trên trang ứng dụng của bạn:

BROADCAST_CONNECTION=pusher
PUSHER_APP_ID=YOUR_APP_ID
PUSHER_APP_KEY=YOUR_APP_KEY
PUSHER_APP_SECRET=YOUR_APP_SECRET
PUSHER_HOST=ws.hibira.io
PUSHER_PORT=443
PUSHER_SCHEME=https
PUSHER_APP_CLUSTER=ap

VITE_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
VITE_PUSHER_HOST="${PUSHER_HOST}"
VITE_PUSHER_PORT="${PUSHER_PORT}"
VITE_PUSHER_SCHEME="${PUSHER_SCHEME}"

Laravel 10 trở về trước dùng BROADCAST_DRIVER=pusher thay cho BROADCAST_CONNECTION. File config/broadcasting.php mặc định đã truyền host, port và scheme cho thư viện Pusher; nếu file của bạn được viết tay, hãy kiểm tra options có đọc các giá trị đó, để không có gì trỏ tới pusher.com.

Bước 2: cấu hình Echo

// resources/js/echo.js
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: 'ap',
  wsHost: import.meta.env.VITE_PUSHER_HOST,
  wsPort: import.meta.env.VITE_PUSHER_PORT,
  wssPort: import.meta.env.VITE_PUSHER_PORT,
  forceTLS: import.meta.env.VITE_PUSHER_SCHEME === 'https',
  enabledTransports: ['ws', 'wss'],
});

Bước 3: broadcast event tới một khách hàng

class OrderShipped implements ShouldBroadcast
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public function __construct(public Order $order) {}

    public function broadcastOn(): array
    {
        return [new PrivateChannel('orders.'.$this->order->id)];
    }
}

// routes/channels.php
Broadcast::channel('orders.{id}', fn ($user, $id) => $user->orders()->whereKey($id)->exists());

// Wherever the order ships
OrderShipped::dispatch($order);
Echo.private(`orders.${orderId}`)
  .listen('OrderShipped', (e) => showStatus(e.order));

Event ShouldBroadcast đi qua hàng đợi của bạn, nên phải có worker (php artisan queue:work) đang chạy. Khi dự án không có hàng đợi, dùng ShouldBroadcastNow để phát ngay trong request.

Bước 4: presence, whisper và toOthers()

Echo.join(`room.${roomId}`)
  .here((users) => online.set(users))
  .joining((user) => online.add(user))
  .leaving((user) => online.remove(user))
  .listen('MessageSent', (e) => messages.append(e.message))
  .listenForWhisper('typing', (e) => typing.show(e.name, 3000));
broadcast(new MessageSent($message))->toOthers();

Whisper là client event: hãy bật Allow client events trong cài đặt ứng dụng. toOthers() bỏ qua tab của chính người gửi bằng header X-Socket-ID, mà Echo tự thêm vào các request Axios; nếu dùng fetch, hãy tự gửi 'X-Socket-ID': Echo.socketId(). Toàn bộ phòng chat có trong tình huống chat.

Bước 5: trạng thái mới nhất cho mọi trang mới mở

Broadcast trên kênh có tên bắt đầu bằng cache-, và ai đăng ký sau cũng nhận ngay event cuối cùng (giữ trong 30 phút):

public function broadcastOn(): array
{
    return [new Channel('cache-scoreboard')];
}
Echo.channel('cache-scoreboard')
  .listen('ScoreUpdated', (e) => board.render(e.scores));

Echo.private('cache-orders.1') hoạt động tương tự và trở thành kênh private-cache-orders.1.

Chuyển từ Reverb hoặc dịch vụ tương thích Pusher khác

Chỉ các giá trị trong .env thay đổi: event, route kênh và mã Echo giữ nguyên, nên bạn có thể chuyển từng môi trường một và chuyển lại cũng dễ như vậy. Trang di chuyển liệt kê những gì cần kiểm tra, và sau khi đăng nhập, hướng dẫn Laravel có sẵn các cài đặt đó với giá trị của ứng dụng bạn.

Mẹo

  • Tạo một ứng dụng cho mỗi môi trường (local, staging, production) để event thử nghiệm không bao giờ tới người dùng thật.
  • Kênh presence chứa tối đa 100 thành viên. Với phòng lớn hơn, xem khán giả đông.
  • Mỗi broadcast tính là một tin nhắn dù có bao nhiêu trình duyệt nhận; việc chuyển phát và cập nhật presence là miễn phí.