Broadcasting system


Broadcasting system

Introduction

In modern web applications, WebSockets are used to implement real-time, instantly updated user interfaces. When the data on the server is updated, the update information is sent to the client through the WebSocket connection for processing. This is a more reliable and efficient option than constantly polling the application.

To help you build this type of application, Laravel will make it easier to "broadcast" events through WebSocket connections. Broadcasting Laravel events allows you to share the same event name between server-side and client-side JavaScript applications.

{Note} Before learning more about event broadcasting, please make sure you have read all the documentation about Laravel events and listeners.

Configuration

All configurations about event broadcasting are saved in the config/broadcasting.php configuration file. Laravel comes with several broadcast drivers: Pusher, Redis, and a log driver for local development and debugging. Additionally, there is a null driver that allows you to turn off the broadcast system completely. Example configurations for each driver can be found in the config/broadcasting.php configuration file.

Broadcast Service Provider

Before broadcasting an event, you must first register App\Providers\BroadcastServiceProvider. For a new Laravel application, you only need to uncomment the provider in the providers array of the config/app.php configuration file. This provider will allow you to register broadcast authorization routes and callbacks.

CSRF Token

Laravel Echo Requires access to the CSRF token for the current session. You should verify that your application's head HTML element defines the meta tag that contains the CSRF token:

<meta name="csrf-token" content="{{ csrf_token() }}">

Requirements for drivers

Pusher

If you use Pusher to broadcast events, please use Composer package manager to install Pusher PHP SDK:

composer require pusher/pusher-php-server "~3.0"

Then, you need to configure your Pusher certificate in the config/broadcasting.php configuration file. This file already contains a Pusher example configuration so you can quickly specify your Pusher key, secret, and application ID. The pusher configuration items in the config/broadcasting.php file also allow you to specify additional options supported by Pusher, such as cluster:

'options' => [ 
   'cluster' => 'eu',    
   'encrypted' => true
 ],

when When using Pusher with Laravel Echo, you should specify pusher as required when instantiating the Echo object in the resources/assets/js/bootstrap.js file broadcaster:

import Echo from "laravel-echo"window.Pusher = require('pusher-js');
window.Echo = new Echo({
    broadcaster: 'pusher',
    key: 'your-pusher-key'
 });

Redis

If you use Redis broadcaster, please install the Predis library:

composer require predis/predis

Redis The broadcaster will use Redis for publishing /Subscribe feature to broadcast messages; however, you still need to pair it with a WebSocket server that can receive messages from Redis in order to broadcast messages to your WebSocket channel.

When the Redis broadcaster publishes an event, the event will be published to its designated channel, and the transmitted data is a JSON-encoded string. This string contains the event name, data data, and the user that generated the event socket ID (if available).

Socket.IO

If you want to pair a Redis broadcaster with a Socket.IO server, you need to include the Socket.IO JavaScript client library in your application. You can install it via the NPM package manager:

npm install --save socket.io-client

Then you need to specify the socket.io connector and host when instantiating Echo.

import Echo from "laravel-echo"window.io = require('socket.io-client');
window.Echo = new Echo({
    broadcaster: 'socket.io',
    host: window.location.hostname + ':6001'
 });

Finally, you need to run a Laravel-compatible Socket.IO server. Laravel does not officially have a built-in Socket.IO server implementation; however, you can choose a community-driven and maintained project tlaverdure/laravel-echo-server, currently hosted on GitHub.

Requirements for queues

Before you start broadcasting events, you also need to configure and run a queue listener. All event broadcasting is done through queue tasks, so application response time will not be significantly affected.

Concept Overview

Laravel's event broadcast allows you to use driver-based WebSockets to broadcast server-side Laravel events to Client-side JavaScript application. The current Laravel comes with Pusher and Redis drivers. By using Laravel Echo's Javascript package, we can easily consume events on the client side.

Events are broadcast through "channels", which can be designated as public or private. Any visitor can subscribe to a public channel without authorization or authentication; however, if one wishes to subscribe to a private channel, the user must be authenticated and authorized for the channel.

Using the Sample Program

Before we dive into each component of event broadcasting, let’s start with an electron Let’s take an overview of business websites as an example. We will not discuss the details of configuring Pusher or Laravel Echo, as these are discussed in detail in other chapters of this document.

In our application, we assume there is a page that allows the user to view the delivery status of an order. There is a ShippingStatusUpdated event that will be triggered when the shipping status is updated:

event(new ShippingStatusUpdated($update));

ShouldBroadcast Interface

When users are viewing their orders, we don't want them to have to refresh the page to see the status updates. We hope to proactively broadcast update information to clients once there is an update. So, we must mark the ShippingStatusUpdated event to implement the ShouldBroadcast interface. This will cause Laravel to broadcast the event when it is fired:

<?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{  
     /**
     * 有关配送状态更新的信息。
     *
     * @var string
     */  
   public $update;
  }

ShouldBroadcast The interface requires the event to define a broadcastOn method. This method is responsible for specifying which channels the event is broadcast to. In the event class generated (via the Artisan command), an empty broadcastOn method is already predefined, so we just need to complete its details. We hope that only the creator of the order can see the status update, so we need to broadcast the event to the private channel bound to this order:

/**
 * 获取事件应该广播的频道。
 *
 * @return array
 */
 public function broadcastOn(){ 
    return new PrivateChannel('order.'.$this->update->order_id);
 }

Authorization Channel

Remember, users can only listen to private channels if they are authorized. We can define channel authorization rules in the routes/channels.php file. In this example, we need to authenticate all users whose view listens to the private order.1 channel to ensure that only the true creator of the order can listen:

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

channel The method receives two parameters: the channel name and a callback function, which indicates whether the user is authorized to listen to the channel by returning true or false.

All authorization callbacks receive the currently authenticated user as the first parameter, and any additional wildcard parameters as subsequent parameters. In this example, we use the {orderId} placeholder to indicate that the "ID" part of the channel name is a wildcard.

Listening to event broadcasts

Next, all that's left is to listen for events in the JavaScript application. We can do this with Laravel Echo. First, we use the private method to subscribe to the private channel. Then, use the listen method to listen to the ShippingStatusUpdated event. By default, all public properties of the event will be included in the broadcast event:

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

Define broadcast events

To tell Laravel that a given event needs to be broadcast, you only need to implement the Illuminate\Contracts\Broadcasting\ShouldBroadcast interface in the event class. This interface has been imported into all event classes generated by the framework, so you can easily add it to your own events.

ShouldBroadcast The interface requires you to implement a method: broadcastOn. This method returns a channel or an array of channels to which the event will be broadcast. These channels must be instances of Channel, PrivateChannel, or PresenceChannel. Channel represents a public channel that any user can subscribe to, while PrivateChannels and PresenceChannels represent private channels that require 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;  
     /**
     * 创建一个新的事件实例。
     *
     * @return void
     */ 
      public function __construct(User $user) 
         {      
             $this->user = $user; 
         }   
      /**
     *获得事件广播的频道。
     *
     * @return Channel|array
     */   
   public function broadcastOn()  
     {     
        return new PrivateChannel('user.'.$this->user->id);   
      }
 }

Then, you just trigger the event as you normally would. Once the event is triggered, a queue task will automatically broadcast the event to the broadcast driver you specify.

Broadcast name

Laravel will use the event's class name as the broadcast name by default to broadcast events. However, you can also define a broadcastAs method in the event class to customize the broadcast name:

/**
 * 事件的广播名称。
 *
 * @return string
 */
 public function broadcastAs(){
     return 'server.created';
  }

If you use the broadcastAs method to customize the broadcast name, you should Make sure you add a . prefix when you register the listener. This will instruct Echo not to prepend the event with the application's namespace:

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

Broadcast data

When a When an event is broadcast, all of its public properties are automatically serialized and broadcast as the event payload, allowing you to access all of the event's public data in your JavaScript application. For example, if your event had a single public $user property that contained an Eloquent model, then the event's broadcast payload would be:

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

However, if you For more fine-grained control over your broadcast payload, you can add a broadcastWith method to your event. This method returns an array of data you want to broadcast as the event payload:

/**
 * 指定广播数据。
 *
 * @return array
 */
 public function broadcastWith(){
     return ['id' => $this->user->id];
 }

Broadcast Queue

By default, each broadcast event will be pushed to the default queue corresponding to the default queue connection specified in the queue.php configuration file. You can define a broadcastQueue property in the event class to customize the queue used by the broadcaster. This property requires you to specify the queue name you want to use when broadcasting:

/**
 * 事件被推送到的队列名称。
 *
 * @var string
 */
 public $broadcastQueue = 'your-queue-name';

If you want to use the sync queue instead of the default queue driver to broadcast events, you can implement ShouldBroadcastNow Interface instead of ShouldBroadcast:

<?php
  use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;
  class ShippingStatusUpdated implements ShouldBroadcastNow{ 
     //
  }

Broadcast conditions

Sometimes, you You want to broadcast your event only if a given condition is true. You can define these conditions by adding a broadcastWhen method to the event class:

/**
 * 判定事件是否可以广播。
 *
 * @return bool
 */
 public function broadcastWhen(){ 
    return $this->value > 100;
 }

Authorized Channel

For private channels, users can only monitor after being authorized. The implementation process is that the user initiates an HTTP request carrying the channel name to your Laravel application, and your application determines whether the user can listen to the channel. When using Laravel Echo, HTTP requests to authorize subscriptions to private channels are sent automatically; however, you still need to define appropriate routes to respond to these requests.

Define Authorization Route

Fortunately, in Laravel we can easily define routes to respond to channels Authorization request. In the BroadcastServiceProvider that comes with Laravel, you can see the call to the Broadcast::routes method. This method will register the /broadcasting/auth route to handle authorization requests:

Broadcast::routes();

Broadcast::routes The method will automatically place its routes into the web In the middleware group; however, if you want to customize the specified attributes, you can pass an array of routing attributes to this method:

Broadcast::routes($attributes);

Custom authorization endpoint

By default, Echo will use the /broadcasting/auth endpoint to authorize channel access. However, you can specify your own authorization endpoint by passing the authEndpoint configuration option to the Echo instance:

window.Echo = new Echo({
    broadcaster: 'pusher',
    key: 'your-pusher-key',
    authEndpoint: '/custom/endpoint/auth'
 });

Define authorization callback

Next, we need to define the logic that is actually used to handle channel authorization. This logic is completed in the routes/channels.php file that comes with the application. In this file, you can use the Broadcast::channel method to register the channel authorization callback:

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

channel The method receives two parameters: the channel name and a callback Function, this callback indicates whether the user is authorized to listen to the channel by returning true or false.

All authorization callbacks receive the current authenticated user as the first parameter, and any additional wildcard parameters as subsequent parameters. In this example, we use the {orderId} placeholder to indicate that the "ID" part of the channel name is a wildcard.

Authorization Callback Model Binding

Just like HTTP routing, channel routing can also take advantage of explicit or implicit routing model binding. For example, you can request to receive a real Order model instance instead of a string or numeric order ID:

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

Authorization callback verification

Private channels and online broadcast channels authenticate the current user identity through the application's default authorization verification. If the user is not authenticated for authorization, channel authorization is automatically denied and the authorization callback is never executed. However, you can assign multiple custom guards to authenticate incoming requests when necessary: ​​

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

Define Channel Class

If your application uses many different channels, your routes/channels.php file may become very large. So, you can use channel classes instead of using closures to authorize channels. To generate a channel class, use the make:channel Artisan command. This command will place a new channel class in the App/Broadcasting directory.

php artisan make:channel OrderChannel

Next, register your channel in your routes/channels.php file:

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

Finally, you can put the channel's authorization logic into the channel In the join method of the class. The join method will hold the same logic you would normally put in the channel authorization closure. Of course, you can also take advantage of channel model binding:

<?php
    namespace App\Broadcasting;
    use App\User;use App\Order;
    class OrderChannel{   
     /**
     * 创建一个新的频道实例。
     *
     * @return void
     */ 
     public function __construct()  
       {     
          //  
        }   
     /**
     * 认证用户的频道访问权限。
     *
     * @param  \App\User  $user
     * @param  \App\Order  $order
     * @return array|bool
     */    
    public function join(User $user, Order $order) 
       {     
          return $user->id === $order->user_id;  
        }
   }

{Note} Like many other classes in Laravel, channel classes are automatically resolved through the service container. Therefore, you can type-hint the required dependencies on your channel class in its constructor.

Broadcast event

After defining an event and marking it to implement the ShouldBroadcast interface, all you have to do is to trigger the event through the event function. The event dispatcher recognizes events tagged that implement the ShouldBroadcast interface and pushes them to a queue for broadcast:

event(new ShippingStatusUpdated($update));

Broadcast to others only

When creating an application that uses event broadcasting, you can use the broadcast function instead of event. Like the event function, the broadcast function distributes events to server-side listeners:

broadcast(new ShippingStatusUpdated($update));

However, the broadcast function also allows you to toOthers method to exclude the current user from being a broadcast receiver:

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

To better understand when to use the toOthers method, let’s assume we have a task List application, users can create new tasks by entering the task name. To create a new task, your application needs to make a request to a /task route, which broadcasts the creation of the task and returns a JSON response for the new task. When your JavaScript application receives the response from the route, it will insert the new task directly into the task list, like this:

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

However, don’t forget, we also broadcast the creation of the task. If your JavaScript application is listening to this event in order to add a task to the task list, there will be duplicate tasks in the task list: one from the route response and one from the broadcast. You can solve this problem by telling the broadcaster not to broadcast the event to the current user using the toOthers method.

{Note} In order to call the toOthers method, your event must use the Illuminate\Broadcasting\InteractsWithSockets trait.

Configuration

When you initialize a Laravel Echo instance, a socket ID is assigned to the connection. If you use Vue and Axios, this socket ID will automatically be added to every outgoing request in the X-Socket-ID header middle. Then, when you call the toOthers method, Laravel will take out the socket ID from the request header and tell the broadcaster not to broadcast any messages to the connection with this socket ID.

If you are not using Vue and Axios, you will need to manually configure your JavaScript application to send the X-Socket-ID request header. You can use the Echo.socketId method to get the socket ID:

var socketId = Echo.socketId();

##Receive Broadcast

Install Laravel Echo

Laravel Echo is a JavaScript library. With this library, it becomes very easy to subscribe to channels and listen to events broadcast by Laravel. You can install Echo through the NPM package manager. In this case, since we will be using the Pusher broadcaster, we will also install the pusher-js package:

npm install --save laravel-echo pusher-js

After installing Echo, you can Create a brand new Echo instance. An ideal place to do this is at the bottom of the resources/js/bootstrap.js file that comes with the Laravel framework:

import Echo from "laravel-echo"
window.Echo = new Echo({
    broadcaster: 'pusher',
    key: 'your-pusher-key'
  });

When you use pusher When using the connector to create an Echo instance, you can also specify cluster and whether the connection needs to be encrypted:

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

Use an existing client instance

If you already have a Pusher or Socket.io client instance that you want Echo to use, you can pass it to Echo via the client configuration option:

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

Listen for events

After installing and instantiating Echo, you can start listening for event broadcasts. First, use the channel method to get a channel instance, and then call the listen method to listen to the specified event:

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

If you want to listen to events on a private channel, please Use the private method. You can listen to multiple events on a single channel by chaining the listen method:

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

Exit Channel

To leave a channel, you can call the leaveChannel method on the Echo instance:

Echo.leaveChannel('orders');

If you want to leave a private channel and an online channel, you can call leave Method:

Echo.leave('orders');

Namespace

You may have noticed that in the above example, We did not specify the complete namespace for the event class. This is because Echo will default events to be in the App\Events namespace. However, you can pass a namespace configuration item to specify the root namespace when instantiating Echo:

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

In addition, you can add # to the event class when using Echo to subscribe to events. ##. prefix. This allows you to specify the fully qualified class name:

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

##Presence Channel

Presence Channel is built on private It builds on the security of a channel and provides an additional feature: knowing who is subscribed to the channel. This makes it very easy to build powerful, collaborative applications, such as when one user is browsing a page, notifying other users who are browsing the same page.

Authorized Presence Channels

All presence channels are also private channels; therefore, users must be authorized before they can access . However, when defining the authorization callback function for the presence channel, if a user has joined the channel, true should not be returned, but an array of information about the user should be returned.

The data returned by the authorization callback function can be used by presence channel event listeners in your JavaScript application. If the user is not authorized to join the presence channel, then you should return false or null:

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

Join Presence Channel

You can use Echo's join method to join the presence channel. The join method will return an object that implements PresenceChannel, allowing you to subscribe to here and joining# by exposing the listen method. ## and leaving events.

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

here The callback function will be executed immediately after you successfully join the channel and receive an array of user information containing all other users currently subscribed to the channel. The joining method will be executed when a new user joins the channel, and the leaving method will be executed when the user exits the channel.

Broadcasting to Presence Channels

Presence channels can receive events just like public and private channels. Using the example of a chat room, we might want to broadcast the

NewMessage event to the chat room's presence channel. To implement this, we will return a PresenceChannel instance from the event's broadcastOn method:

/**
 * 获得事件广播的频道。
 *
 * @return Channel|array
 */
 public function broadcastOn(){
     return new PresenceChannel('room.'.$this->message->room_id);
   }

Just like public or private events, presence channel events can also be used

broadcast function to broadcast. Similarly, you can also use the toOthers method to exclude the current user from broadcast receivers:

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

You can listen to the join event through Echo's

listen method :

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

Client-side events

{Note} When using Pusher, if you want to send client-side events, you must set the ” section to enable the “Client Events” option.

Sometimes, you may want to broadcast an event to other connected clients without notifying your Laravel application. This is especially useful when handling notifications for "typing in progress" things, such as alerting users of your app that another user is typing information on a given screen.

You can use Echo's

whisper

method to broadcast client events:

Echo.private('chat') 
   .whisper('typing', {
        name: this.user.name    
    });
You can use the

listenForWhisper

method to listen for client events:

Echo.private('chat')
    .listenForWhisper('typing', (e) => {
        console.log(e.name); 
     });

##Message Notification
By pairing event broadcasts with message notifications, your JavaScript application can Refresh the page to receive new message notifications. Before doing this, make sure you have read the documentation on how to use broadcast notification channels.

After configuring the message notification using the broadcast channel, you can use Echo's

notification

method to listen for broadcast events. Remember, the channel name should match the class name of the entity receiving the message notification:

Echo.private(`App.User.${userId}`) 
   .notification((notification) => {
        console.log(notification.type); 
      });
In this example, all messages sent to App\User

are sent via the

broadcast channel Message notifications of the instance will be received by the callback. An authorization callback function for the App.User.{id} channel has been included in the built-in BroadcastServiceProvider of the Laravel framework. This article was first published on the LearnKu.com

website.