Event Broadcasting

Giới thiệu

Trong nhiều ứng dụng applications hiện nay, WebSockets được sử dụng để làm realtime, live-updating user interfaces. Khi một số dữ liệu được cập nhật trên the server, amột tin nhắn sẽ gửi cho WebSocket để xử lý phía bên client. Nó cung cấp rất tuyệt, hiệu quả cho những ứng dụng cần realtime.

Để hỗ trợ bạn thực hiện những ứng dụng như vậy, Laravel tạo một "broadcast" events kết nối với WebSocket. Broadcasting của Laravel events acho phép bạn chia sẽ những tên event giữa server-side code và client-side JavaScript của ứng dụng.

Trước khi tìm hiểu về event broadcasting, bạn nên tìm hiểu hết phần events and listeners.

Cấu hình

Tất cả cấu hình event broadcasting của ứng dụng lưu tại config/broadcasting.php. Laravel hỗ trợ vài broadcast drivers như: Pusher, Redis, và một log driver cho local development và debugging. Ngoài ra, một null driver được thêm vào cho phép bạn có thể disable broadcasting. Một cấu hình ví dụ cho nhưng drivers đó trong file config/broadcasting.php.

Broadcast Service Provider

Trước đó, bất cứ broadcasting events nào, bạn cũng cần phải đăng ký App\Providers\BroadcastServiceProvider. Trong ứng dụng mới, bạn chỉ cần bỏ comment những provider trong mảng providers của file cấu hình config/app.php. Provider này cho phép bạn đăng ký broadcast authorization routes và callbacks.

CSRF Token

Laravel Echo bạn cần truy cập vào CSRF token của session. Nếu tồn tại, Echo sẽ bắt lấy token từ đối tượng Laravel.csrfToken JavaScript. Đối tượng này sẽ được định nghĩa trong resources/views/layouts/app.blade.php layout mà được tạo bằng lệnh make:auth Artisan. Nếu bạn không sử dụng layout này, bạn có thể định nghĩa tag meta tron thẻ head HTML:

<meta name="csrf-token" content="Nr3ogVQkqsnOJtE5anatpnKGyMv55QkIW0MvQavX">

Điều kiện bắt buộc

Nếu bạn sử dụng events trên Pusher, bạn cần cài Pusher PHP SDK sử dụng Composer:

composer require pusher/pusher-php-server

Tiếp thep, bạn cần cấu hình Pusher credentials trong file config/broadcasting.php. Một ví dụ về cấu hình Pusher đã được thêm vào, cho phép bạn dễ dàng tùy biến Pusher key, secret, và ID của ứng dụng.

Khi sử dụng Pusher và Laravel Echo, bạn cần chỉ định pusher như broadcaster bạn muốn và cài đặt Echo instance:

import Echo from "laravel-echo"

window.Echo = new Echo({
    broadcaster: 'pusher',
    key: 'your-pusher-key'
});

Redis

Nếu bạn sử dụng Redis broadcaster, bạn cần cài thư viện Predis:

composer require predis/predis

Redis broadcaster sẽ broadcast tin nhắn sử dụng tính năng Redis' pub / sub; tuy nhiên, bạn cần phải ghép nó với WebSocket server để có thể nhận tin nhắn từ Redis và broadcast trên kênh WebSocket.

Khi Redis broadcaster xuất bản một event, nó sẽ được xuất bản tên kênh được chỉ của event và payload sẽ được JSON encoded chứa tên của event, một data payload, và user được tạo socket ID của event (nếu áp dụng).

Socket.IO

Nếu bạn đang sử dụng Redis broadcaster với Socket.IO server, bạn sẽ cần phải thêm thư viện Socket.IO JavaScript client trong thẻ head HTML của ứng dụng:

<script src="https://cdn.socket.io/socket.io-1.4.5.js"></script>

Tiếp theo, bạn cần thể hiện Echo với người nối socket.io và một host. Ví dụ, nếu ứng dụng của bạn và socket server đang chạy trên tên miên app.dev bạn cần thể hiện Echo như sau:

import Echo from "laravel-echo"

window.Echo = new Echo({
    broadcaster: 'socket.io',
    host: 'http://app.dev:6001'
});

Cuối cùng, bạn cần chạy Socket.IO server. Laravel không cung cấp thực thi Socket.IO server; tuy nhiên, cộng đồng Socket.IO server đã có và duy trì tại kho tlaverdure/laravel-echo-server GitHub.

Điều kiện Queue

Trước khi broadcasting events, bạn cần phải cấu hình và chạy queue listener. Tất cả event broadcasting được hoàn thành qua queued jobs do dó thời gian response của ứng dụng không bị ảnh hưởng nghiêm trọng.

Concept Overview

Event broadcasting của laravel cho phép bạn broadcast server-side events đến client-side JavaScript của ứng dụng sử dụng cách tiếp cận driver-based WebSockets. Hiện tại, Laravel đã có Pusher và Redis drivers. Events có thể dễ dàng tiêu thụ client-side bằng cách sử dụng Laravel Echo Javascript package.

Events được broadcast trên "các kênh", nó có thể được chỉ định public hoặc private. Bất kỳ người dùng vào ứng dụng có thể theo dõi kênh kệnh public mà không cần authentication hoặc authorization; tuy nhiên, đối với những theo dõi kênh private, người dùng cần phải authenticated và authorized để lắng nghe kênh.

Using Example Application

Trước khi tiếp cận component của event broadcasting, hãy nhìn một cách tổng thể sử dụng một trang thương mại điện tử như một ví dụ. Chúng ta không thảo luận chi tiết về cấu hình Pusher or Laravel Echo vì nó đã được nói ở trong tài liệu này rồi.

Trong ứng dụng, giả sử chúng ta có một trang cho người dùng xem tình trạng vận chuyển của các đơn hàng của họ. Giả sử là một event ShippingStatusUpdated được bắn khi tình trang vận chuyển cập nhật được xử lý bởi ứng dụng:

event(new ShippingStatusUpdated($update));

The ShouldBroadcast Interface

Khi người dùng xem những order của họ, chúng ta không muốn họ phải refresh lại trang thìm mới nhìn thấy cập nhật. Thay vì, chúng ta muốn broadcast cập nhật vào ứng dụng nhưn là họ được tạo. Vì vậy, chúng ta cần đánh dấu event ShippingStatusUpdated với ShouldBroadcast interface. Nó sẽ hướng dẫn cho Laravel broadcast ra event kho nó được bắn:

<?php

namespace App\Events;

use Illuminate\Broadcasting\Channel;
use Illuminate\Queue\SerializesModels;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;

class ShippingStatusUpdated implements ShouldBroadcast
{
    //
}

ShouldBroadcast interface yêu cầu event định nghĩa trong hàm broadcastOn. Hàm này có trả về những kênh mà event được broadcast. Một stub đã được định nghĩa trong class event được tạo, vì vậy chúng ta chỉ cần điền nó trong chi tiết.Chúng ta chỉ muốn người tạo order có thể xem trạng thái cập nhật, vì vậy chúng ta sẽ broadcast eventtrong một kênh private mà gắn liền với thứ tự order:

/**
 * Get the channels the event should broadcast on.
 *
 * @return  array
 */
public function broadcastOn()
{
    return new PrivateChannel('order.'.$this->update->order_id);
}

Authorizing Channels

Nhớ rằng, người dùng phải authorized để lắng nghe kênh private. Chúng ta có thể định nghĩa luật của kênh authorization trong hàm boot của BroadcastServiceProvider. Trong ví dụ này, chúng ta cần verify bất cứ người dùng nào cố gắng lắng nghe trên kênh private order.1 là người tạo order:

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

Hàm channel mcho phép nhận hai tham số: tên của kênh và hàm callback mà trả về true hoặc false chỉ ra liệu người dùn được ủy quyền để lắng nghe kênh.

Tất cả các hàm authorization callbacks nhận người authenticated hiện tại như là tham số thứ nhất và bất kỳ và tham số thứ hai là wildcard . Trong ví dụ này, chúng ta sử dụng ký tự * để chỉ ra rằng "ID" phân của tên kênh là một wildcard.

Listening For Event Broadcasts

Tiếp theo, tất cả những phần còn lại là lắng nghe event trong ứng dụng JavaScript. Chúng ta có thể sử dụng Laravel Echo. Đầu tiên, chúng ta sẽ sử dụng hàm private để đăng ký kênh private. Sau đó, chúng ta sử dụng hàm listen để lắng nghe event cho ShippingStatusUpdated. Mặc định, tất cả các thuộc tính public của event sẽ được thêm bên trong broadcast event:

Echo.private('order.' + orderId)
    .listen('ShippingStatusUpdated', (e) => {
        console.log(e.update);
    });

Defining Broadcast Events

Để cho Laravel biết nếu một event cần được broadcase, triển khai interface Illuminate\Contracts\Broadcasting\ShouldBroadcast interface trong event class. Interface này đã được có trong tất cả các event class được sinh ra bởi framework mà bạn có thể dễ dàng thên nó cho bất kỳ events.

Interfacee ShouldBroadcast yêu cần bạn thực thi một hàm: broadcastOn. Hàm broadcastOn thàm này trả về một mảng tên "các channel" mà event cần được broadcast tới. Các kênh là các instances của Channel, PrivateChannel, hoặc PresenceChannel. Thể hiện của Channel đại diện cho các kênh public mà người dùng có thể đăng ký, trong khi PrivateChannelsPresenceChannels là đại diện cho các kênh private mà yêu cầu channel authorization:

<?php

namespace App\Events;

use Illuminate\Broadcasting\Channel;
use Illuminate\Queue\SerializesModels;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;

class ServerCreated implements ShouldBroadcast
{
    use SerializesModels;

    public $user;

    /**
     * Create a new event instance.
     *
     * @return  void
     */
    public function __construct(User $user)
    {
        $this->user = $user;
    }

    /**
     * Get the channels the event should broadcast on.
     *
     * @return  Channel|array
     */
    public function broadcastOn()
    {
        return new PrivateChannel('user.'.$this->user->id);
    }
}

Sau đó, bạn chỉ cần fire the event như thông thường. Khi mà event đã được bắn ra, một queued job sẽ tự động broadcast event qua broadcast driver đã cấu hình.

Broadcast Name

Mặc định, Laravel sẽ broadcast event sử dụng tên của class event. Tuy nhiên, bạn có thể tùy biến tên broadcast bằng cách định nghĩa ham broadcastAs trong event:

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

Broadcast Data

Khi một event được broadcase, tất cả các thuộc tính public được tự động serialize và broadcast cùng event payload, điều này cho phép bạn lấy bất cứ dữ liệu public nào từ Javascript. Vì thế, ví dụ như, nếu event có một thuộc tính public là $user có chứa thông tin về Eloquent model tương ứng, thì payload của broadcast sẽ kiểu thế này:

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

Tuy nhiên, nếu bạn muốn có quyền kiểm soát tốt hơn với payload này, bạn có thể thêm vào hàm broadcastWith trong event. Hàm này sẽ trả về một mảng dữ liệu mà bạn muốn gửi đi cùng event:

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

Broadcast Queue

Mặc định, mỗi broadcast event được đặt vào một queue mặc địn queue cho kết nối theo chỉ định tại file queue.php. Bạn có thể tùy biến queue sử dụng broadcaster bởi định nghĩa thuộc tính broadcastQueue trong event class. Thuộc tính này nên được chỉ định tên queue bạn muốn sử dụng khi broadcasting:

/**
 * The name of the queue on which to place the event.
 *
 * @var  string
 */
public $broadcastQueue = 'your-queue-name';

Authorizing Channels

Kênh private yêu cầu bạn phải authorize các authenticated người dùng hiện tại thực sự lắng nghe kênh. Điều này được thực hiện bằng cách tạo một HTTP request đến ứng dụng với một tên kênh channel và cho phép ứng dụng của bạn để xác định xem người dùng có thể lắng nghe kênh đó. Khi sử dụng Laravel Echo, HTTP request cho phép authorize vào các kênh private sẽ được tự động thực hiện; tuy nhiên,bạn cần định nghĩa routes thích hợp để đáp ứng những respond này.

Defining Authorization Routes

Laravel làm cho nó rất đơn giản đễ xác định routes đáp ứng respond với kênh authorization requests. Trong BroadcastServiceProvider được thêm vào ứng dụng Laravel, bạn sẽ nhìn thấy gọi một hàmBroadcast::routes. Hàm này sẽ đăng ký /broadcasting/auth route để xử lý các authorization requests:

Broadcast::routes();

Hàm Broadcast::routes sẽ tự động được đặt vào bên trong web middleware group; tuy nhiên, bạn có thể truyền vào một mảng thuộc tính route nếu bạn muốn tùy biến nó:

Broadcast::routes($attributes);

Defining Authorization Callbacks

TIếp theo, chúng ta cần định nghĩa logic mà sẽ được thực hiện việc kênh authorization. Như định nghĩa authorization routes, nó cũng được thực hiện trong hàm boot của BroadcastServiceProvider. Trong hàm này, bạn phải sử dụng hmaf Broadcast::channel để đăng ký kênh authorization callbacks:

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

Hàm channel chấp nhận hai tham số: tên của kênh và một hàm callback trả về true hoặc false chỉ ra người dùng có được lắng nghe kênh hay không.

Tất cả các hàm authorization callbacks nhận authenticated của người dùng hiện tại như là tham số thứ nhất và ngoài ra ký hiệu wildcard là tham số thứ hai. Trong ví dụ này, chúng ta sử dụng ký tự * để chỉ ra "ID" phần của tên kênh như là wildcard.

Broadcasting Events

Khi bạn đã định event và đánh dấu nó với ShouldBroadcast interface, bạn chỉ cần bắn event sử dụng hàm event. Event dispatcher sẽ để ý event được đánh dấu với ShouldBroadcast interface và sẽ queue event cho broadcasting:

event(new ShippingStatusUpdated($update));

Only To Others

Khi xây dựng ứng dụng mà có event broadcasting, bạn có thể thay thế hàm event bằng hàmbroadcast. Giống như hàm event, hàm broadcast dispatches event vào server-side listeners:

broadcast(new ShippingStatusUpdated($update));

Tuy nhiên, hàm broadcast ngoài ra còn có hàm toOthers cho phép bạn loại trừ người dùng từ user người nhận broadcast:

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

Để hiểu hơn khi nào bạn cần sử dụng hàm toOthers, hãy tưởng tượng một danh sách task nơi mà người dùng có thể tạo một task mới bằng cách nhập tên task. Đê tạo một task, ứng dụng của bạn phải tạo một request tới đích /task nơi mà broadcasts trả về JSON tương ứng với task đó. Khi ứng dụng JavaScript nhận response từ end-point, nó phải trực tiếp thêp task vào trong danh sách task như sau:

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

Tuy nhiên, nhớ rằng chúng ta có thể broadcast sáng tạo task . Nếu ứng dụng JavaScript lắng nghe event này trong order thêm tasks vào danh sách task, bạn sẽ bị duplicate tasks trong danh sách: một từ end-point và một từ broadcast.

Bạn có thể giải quyết nó bằng hàm toOthers để hướng dẫn broadcaster không được broadcast event đến người dùng hiện tại.

Cấu hình

Khi bạn khởi tạo một Laravel Echo instance, một socket ID được gán với kết nối. Nếu bạn sử dụng Vue và Vue Resource, socket ID sẽ tự động được gán vào outgoing request như một X-Socket-ID header. Sau đó, khi bạn gọi hàm toOthers, Laravel sẽ tự động lấy ra socket ID từ header và hướng dẫn broadcaster không được broadcast đến bấy kỳ kết nối với socket ID đó.

Nếu bạn không sử dụng Vue và Vue Resource, bạn sẽ cần phải cấu hình ứng dụng JavaScript để gửu X-Socket-ID header. Bạn cần phải nhận một socket ID sử dụng hàmEcho.socketId:

var socketId = Echo.socketId();

Receiving Broadcasts

Installing Laravel Echo

Laravel Echo là thư viện JavaScript nó dùng để đăng ký kênh và lắng nghe event events broadcast bởi Laravel. Bạn cần phải cài Echo qua NPM package manager. Trong ví dụ này, chúng ta còn cài đặt pusher-js pvì chúng ta cần sử dụng Pusher broadcaster:

npm install --save laravel-echo pusher-js

Khi Echo được cài đặt, bạn đã sẵn sàng bắt đầu Echo instance trong ứng dụng JavaScript. Một vị trí tốt để làm điều này là ở dưới cùng file resources/assets/js/bootstrap.js được thêm vào bởi Laravel framework:

import Echo from "laravel-echo"

window.Echo = new Echo({
    broadcaster: 'pusher',
    key: 'your-pusher-key'
});

Khi tạo Echo instance sử dụng pusher connector, bạn phải chỉ định cluster cũng như việc kết nối được mã hóa:

window.Echo = new Echo({
    broadcaster: 'pusher',
    key: 'your-pusher-key',
    cluster: 'eu',
    encrypted: true
});

Listening For Events

Khi bạn đã cài đặt và instantiated Echo, bạn có thể sẵn sàng bắt đầu lắng nghe event broadcasts. Đầu tiên, sử dụng hàm channel để nhận một instance của kênh, sau đó gọi hàm listen để lắng nghe event được chỉ định:

Echo.channel('orders')
    .listen('OrderShipped', (e) => {
        console.log(e.order.name);
    });

Nếu bạn muốn lắng nghe events trên kênh private, sử dụng hàm private thay thế. Bạn có thể tiếp tục gọi đến hàm listen để lắng nghe nhiều events trên một kênh:

Echo.private('orders')
    .listen(...)
    .listen(...)
    .listen(...);

Leaving A Channel

Để thoát một, bạn có thể gọi hàm leave trong Echo instance:

Echo.leave('orders');

Namespaces

Bạn cần phải chú ý trong ví dụ trên là chúng ta không chỉ định tên namespace đầy đủ cho event classes. Bời vì Echo sẽ tự động giả sử events được để tại App\Events namespace. Tuy nhiên, bạn có thể cấu hình namespace gốc khi bạn instantiate Echo bởi truyền một cấu hình namespace:

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

Ngoài ra, bạn có thể thêm tiền tố event classes với ký hiệu . khi đăng ký sử dụng Echo. Nó sẽ cho phép bạn luôn luôn sử dụng tên class đầy đủ:

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

Presence Channels

Khi xây dựng các kênh presence trên sự bảo mật của kênh private trong khi tiếp xúc với các tính năng bổ sung cho người đăng ký kênh . Điều này làm cho nó dễ dàng để mạnh mẽ, tính năng cộng tác của ứng dụng như là một thông báo cho người dùng khác đang xem một trang.

Authorizing Presence Channels

Tất cả các kênh presence là các kênh private; do đó, người dùng phải authorized to access them. Tuy nhiên, khi định nghĩa hàm authorization callbacks cho kênh presence, bạn sẽ không trả về true nếu người dùng authorized để vào kênh. Thay vì, bạn nên trả về một mảng dữ liệu của người dùng.

Dữ liệu trả về bởi authorization callback sẽ được tạo trong kênh presence event listeners trong ứng dụng JavaScript. Nếu người dùng không authorized đề vào kênh presence, bạn nên trả về false hoặc null:

Broadcast::channel('chat.*', function ($user, $roomId) {
    if ($user->canJoinRoom($roomId)) {
        return ['id' => $user->id, 'name' => $user->name];
    }
});

Joining Presence Channels

Để vào một kênh presence, bạn sử dụng hàm join method. The join của Echo sẽ trả về một PresenceChannel, cùng với hàm listen, cho phép bạn đăng ký here, joining, và leaving events.

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

Hàm here callback sẽ được thực thi trực tiếp một khi kênh được gia nhập thành công, và sẽ nhận được một mảng chứa thông tin của người dùng cho tất cả những người dùng khác đang đăng ký kênh này. Hàm joining sẽ được thực thi khi một người dùng với vào một kênh, trong khi hàm leaving msẽ được thực thi khi người dùng rời khỏi kênh.

Broadcasting To Presence Channels

Kênh presence có thể nhận các events như kênh public hoặc private. Sử dụng ví dụ có một chatroom, chúng ta một broadcast NewMessage events từ phòng của kênh presence. Để làm nó, chúng ta sẽ trả về một instance của PresenceChannel từ hàm broadcastOn của event:

/**
 * Get the channels the event should broadcast on.
 *
 * @return  Channel|array
 */
public function broadcastOn()
{
    return new PresenceChannel('room.'.$this->message->room_id);
}

Như các public hoặc private events, kênh presence events có thể được broadcast bằng cách sử dụng hàm broadcast. Như những events khác, bạn có thể sử dụng hàm toOthers để loại người dùng hiện tại nhận từ broadcast:

broadcast(new NewMessage($message));

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

You may listen for the join event via Echo's listen method:

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

Notifications

Bằng cách ghép event broadcasting với notifications, ứng dụng JavaScript có thể nhận notifications mới mà họ không phải refresh lại trang. Đầu tiên, bản phải đọc hết the broadcast notification channel.

Khi bạn đã cấu hình notification để sử dụng kênh broadcast, bạn có thể lắng nghe broadcast events bằng cách dùng hàm notification của Echo. Nhớ rằng, tên kênh nên giống với tên class của thực thể nhận notifications:

Echo.private('App.User.' + userId)
    .notification((notification) => {
        console.log(notification.type);
    });

Trong ví dụ này, tất cả notifications được gửi tới App\User instances qua kênh broadcast sẽ được nhận bởi hàm callback. Một kênh authorization callback cho App.User.* kênh được theeo vào BroadcastServiceProvider mặc định của Laravel framework.

Nguồn: https://laravel.com/docs/5.3/broadcasting