Implementation of event broadcast between server and client in Larravel


brief introduction

In many modern Web applications, Web Sockets are used to implement the user interface of real-time update. When some data is updated on the server, a message is usually sent to the client for processing through the Web socket connection. This provides us with a more powerful and effective choice to continuously pull application updates.

To help you build such an application, Laravel lets you connect to the broadcast through the websocket event Make it easy. Broadcast Larravel events allows you to share the same event name between the server and client JavaScript frameworks.

Note: Make sure you have read and understood Laravel before going into the event broadcast Events and listeners Related documents.

to configure

All applied event broadcast configuration options are stored in config/broadcasting.php Configuration file. Larravel supports multiple broadcast drivers out of the box: Pusher Redis And a service for local development and debugging log Drive. In addition, a null The driver is used to completely prohibit event broadcasting. Each drive config/broadcasting.php There is a configuration example in the configuration file.

Broadcast Service Provider

Before broadcasting any event, you need to register App\Providers\BroadcastServiceProvider In the newly installed Larravel app, you just need to cancel config/app.php In configuration file providers The comments before the corresponding service provider in the array are sufficient. This provider allows you to register broadcast authorization routes and callbacks.

CSRF token

Laravel Echo You need to access the CSRF token of the current session. If it is valid, Echo will access the CSRF token from the JavaScript variable Laravel.csrfToken Get the token in. If you've run Artisan commands make:auth The object is defined in the resources/views/layouts/app.blade.php In the layout file. If you do not use this layout, you can use the HTML elements in the application head Such a meta label:

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

Drive preparatory knowledge

Pusher

If you are going to pass Pusher To broadcast events, you need to use the Composer package manager to install the corresponding Pusher PHP SDK:

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

Next, you need to config/broadcasting.php Configure your Pusher certificate in the configuration file. A configured Pusher example has been included in this file. You can modify it according to this template by specifying your own Pusher key, secret and application ID. config/broadcasting.php Filed pusher The configuration also allows you to specify additional options , for example cluster

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

Using Pusher and Laravel Echo When you need to resources/assets/js/bootstrap.js Specify when installing an Echo instance in the file pusher As expected broadcast:

 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 radio, you need to install the Predis library:

 composer require predis/predis

Redis radio uses Redis's Publish/subscribe function Broadcast; However, you need to pair it with a Websocket server that can accept Redis messages to broadcast messages to the Websocket channel.

When Redis broadcasts and publishes an event, the event will be published to the specified channel. The data transmitted is a string in JSON format, including the event name and data details data , and the user who generated the event socket ID.

Socket. IO

If you want to pair Redis radio and socket For the IO server, the HTML elements in the application head Socket IO JavaScript library. When Socket After the IO server is started, it will automatically expose the client JavaScript library through a standard URL. For example, if you run Socket under the same domain name IO and Web applications can access the client JavaScript library in this way:

 <script src="//{{ Request::getHost() }}:6001/socket.io/socket.io.js"></script>

Next, you need to use socket.io Connectors and host To instantiate Echo:

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

Finally, you need to run a compatible Socket IO server. Larave does not have a socket built in IO server implementation. However, there is a socket implemented by a third party IO drive: tlaverdure/laravel-echo-server

Queue preparatory knowledge

Before introducing broadcast events, you need to configure and run a Queue Listener All event broadcasts are completed through queue tasks so that the application response time is not affected.

Concept overview

Laravel's event broadcast allows you to broadcast server-side events to client-side JavaScript applications using the driver based WebSocket. At present, Laravel uses Pusher and Redis drivers, and these events can be driven by JavaScript packages Laravel Echo It is easily consumed on the client side.

Events are broadcast through "channels". These channels can be public or private. Any visitor to the application can register to a public channel without authentication and authorization. However, to register to a private channel, users must be authenticated and authorized to listen to the channel.

Sample application

Before we go deep into each event broadcast component, let's take an e-commerce website as an example to get a general understanding of the whole. We won't discuss it here Pusher and Laravel Echo Configuration details, which will be discussed later in this document.

In our application, suppose we have a page that allows users to view the logistics status of orders. We also assume that when the application updates the order status, a ShippingStatusUpdated event:

 event(new ShippingStatusUpdated($update));

ShouldBroadcast interface

When users view an order, we do not want them to have to refresh the page to view the update status. Instead, we want to broadcast updates to the app when it is created. Therefore, we need to mark ShippingStatusUpdated Event Implementation ShouldBroadcast Interface, so that Larravel will broadcast the event when it is triggered:

 <? 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 { /** *Logistics status update information * * @var string */ public $update; }

ShouldBroadcast The interface requires an event class to define a broadcastOn Method that returns the channel the event will broadcast. When the event class is generated, an empty method stub has been defined. All we need to do is fill in its details. We only want the order creator to be able to view the status update, so we broadcast this event on a private channel bound to the order:

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

Authorized Channel

Remember, users must be authorized to listen to private channels. We can routes/channels.php Channel authorization rules are defined in the file. In this example, we need to verify that any attempt to listen order.1 The user of the channel is really the creator of the order:

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

channel Method receives two parameters: channel name and return true or false This indicates whether the user is authorized to listen to the callback of the channel.

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 {orderId} The placeholder identifies that the ID portion of the channel name is a wildcard.

Monitor event broadcast

The next step is to listen to events in JavaScript. We can use Larravel Echo to do this. First, we use private Method to subscribe to a private channel. Then, we use listen Method monitoring ShippingStatusUpdated event. By default, public properties of all events are included in broadcast events:

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

Define broadcast events

Next, let's decompose the above example application.

To tell Laravel that a given event should be broadcast, it needs to be implemented on the event class Illuminate\Contracts\Broadcasting\ShouldBroadcast Interface. This interface has been imported into the event class generated by the Larravel framework. You just need to add it to the event.

ShouldBroadcast The interface requires you to implement a method: broadcastOn This method should return an event broadcast channel or channel array. These channels must be Channel PrivateChannel or PresenceChannel Instance of, Channel Channel refers to a public channel that any user can subscribe to, while PrivateChannels or PresenceChannels Then the representative needs to Channel Authorization Private channel of:

 <? 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 * @translator laravelacademy.org */ public function __construct(User $user) { $this->user = $user; } /** *Get the channel of event broadcast * * @return Channel|array */ public function broadcastOn() { return new PrivateChannel('user.'.$this->user->id); } }

Then, you just need to be normal Trigger this event OK. Once the event is triggered, Queue Task The event is automatically broadcast by the specified broadcast driver.

Broadcast name

By default, Larravel will use the class name of the event to broadcast the event. However, you can define broadcastAs Method to customize broadcast name:

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

If you use broadcastAs Method to broadcast events. It is necessary to ensure that the listener is registered with . Prefix character. This will tell Echo not to add the namespace of the application before the event:

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

Broadcast data

If an event is broadcast, all its public Properties will be serialized and broadcast automatically according to the payload, allowing you to access all public Data, for example, if your event has a separate $user Attribute, broadcast load is defined as follows:

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

However, if you want to have more granular control over the broadcast load, you can add broadcastWith Method to event, this method will return the array data you want to broadcast through the event:

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

Broadcast Queue

By default, each broadcast event will be pushed to the configuration file queue.php The default queue specified in is connected to the corresponding default queue. You can define a broadcastQueue Property to customize the queue used by the broadcast. This attribute needs to specify the queue name you want to use when broadcasting:

 /** *The name of the queue where the event is pushed * * @var string */ public $broadcastQueue = 'your-queue-name';

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

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

Broadcast condition

Sometimes you want to specify true The event is broadcast only on the premise of. You can add broadcastWhen Method to event class to define these conditions:

 /** *Determine whether the event is broadcast * * @return bool */ public function broadcastWhen() { return $this->value > 100; }

Authorized Channel

Private channels require you to authorize the channels that the current authenticated user can listen to. This can be achieved by sending an HTTP request containing the channel name to Larravel, and then letting the application judge whether the user can listen to the channel. use Laravel Echo When you subscribe to a private channel, HTTP requests will be sent automatically. However, you also need to define corresponding routes to respond to these requests.

Define authorization routes

Fortunately, it is easy to define the route to respond to channel authorization requests in Larvel BroadcastServiceProvider Middle, you can see Broadcast::routes Method, which will register /broadcasting/auth Routes to process authorization requests:

 Broadcast::routes();

Broadcast::routes Method will automatically place the route to web Middleware group, however, you can also pass an array of routing attributes to this method to customize the assigned attributes:

 Broadcast::routes($attributes);

Define authorization callback

Next, we need to define the logic for executing channel authorization routes/channels.php File. In this method, you can use 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 Method receives two parameters: channel name and return true or false To identify whether the user is authorized to listen to the callback of the channel.

All authorization callbacks receive the current authenticated user as the first parameter and any additional wildcard parameters as other parameters. In this example, we use placeholders {orderId} The ID part that identifies the channel name is a wildcard.

Authorization callback model binding

Like HTTP routing, channel routing can also use implicit and explicit Routing model binding For example, we can directly receive a real Order Model instance, not order ID in string or numeric format:

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

Broadcast Events

Define the event and mark its implementation ShouldBroadcast After the interface, all you have to do is use the event Method triggers the event. The event distributor will focus on whether the event has been implemented ShouldBroadcast Interface. If it is, push it to the broadcast queue:

 event(new ShippingStatusUpdated($update));

Only broadcast to others

When building applications that use event broadcasting, you can also use broadcast Function substitution event Functions, and event The functions are the same, broadcast The function distributes the event to the server listener:

 broadcast(new ShippingStatusUpdated($update));

however, broadcast The function also exposes toOthers Method to allow you to exclude the current user from the broadcast receiver:

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

To better understand toOthers To create a task, the application needs to send a request to /task Here, the task creation will be broadcast and a new task in JSON format will be returned. When your JavaScript application receives a response from the server, it will directly insert this new task into the task list:

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

But remember? We also broadcast task creation. If your JavaScript application is listening to this event to add tasks to the task list, duplicate tasks will appear in the list: one from the server and one from the broadcast. You can use toOthers Method, which tells the broadcast not to broadcast the event to the current user.

Note: Events must use Illuminate\Broadcasting\InteractsWithSockets Trait to call toOthers method.

to configure

When you initialize the Laravel Echo instance, you need to assign a socket ID to the connection. If you use Vue and Axios , the socket ID will be X-Socket-ID The header mode is automatically added to each output request. When you call toOthers Method, Laravel will parse the socket ID from the request header and tell the broadcast not to broadcast to the connection with the socket ID.

If you do not use Vue and Axios, you need to manually configure JavaScript application sending X-Socket-ID Request header. You can use Echo.socketId Method to obtain the socket ID:

 var socketId = Echo.socketId();

Receive broadcast

Installing the Laravel Echo

Laravel Echo is a JavaScript library. With it, it will be easy to subscribe channels to listen to events broadcast by Laravel. You can install Echo through the NPM package manager. In this example, we will also install pusher-js Package, because we will use Pusher to broadcast:

 npm install --save laravel-echo pusher-js

After installing Echo, you can create a new Echo instance in the JavaScript of the application. Of course, the best place to do this is in Laravel resources/assets/js/bootstrap.js Bottom of file:

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

Create a pusher You can also specify a cluster And whether the connection needs encryption:

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

Listen for events

After installing and initializing Echo, you can start listening to event broadcasts. First, use the channel Method to obtain a channel instance, and then call listen Method listens 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, you can use private Method, you can still call listen Method listens for multiple events on a single channel:

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

Leave Channel

To leave a channel, you can call leave method:

 Echo.leave('orders');

Namespace

You may have noticed that in the above example, we did not specify the full namespace of the event class, because Echo will default that all events are located in App\Events Namespace. However, you can pass configuration options when instantiating Echo namespace To configure the root namespace:

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

In addition, when using Echo to subscribe to events, you can add . Prefix, so you can specify the complete class name:

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

Presence Channel

Existing channels are built on private channels and provide an additional function: know who has subscribed to the channel. Based on this, we can build powerful and collaborative application functions, such as notifying the current user when other users visit the same page.

Authorization Exists Channel

All existing channels are also private channels, so the user must be Authorize access However, when defining an authorization callback for a channel, if the user is authorized to join the channel, do not return true , instead, you should return an array of data about the user.

The data returned by the authorization callback is used in the existing channel event listener of the JavaScript application. If the user is not authorized to join the existing channel, it should return false or null

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

Add Existing Channel

To join an existing channel, you can use Echo's join method, join Method will return a PresenceChannel Implement and expose listen Method, which allows you to register to here joining and leaving event:

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

here The callback will be executed immediately after the channel is successfully added, and receive an array containing all other user information subscribing to the channel. joining Method will be executed when a new user joins the channel, leaving Method is executed when the user leaves the channel.

Broadcast to existing channels

There are channels that can receive events like public or private channels. Taking chat rooms as an example, we may want to broadcast NewMessage The existence channel from the event to the room. To achieve this function, you need to start from the broadcastOn Method return PresenceChannel example:

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

Like public or private channels, channel events can be used broadcast Function to broadcast. Like other events, you can use toOthers Method Exclude the current user from all users receiving broadcast:

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

You can use Echo's listen Method listening to join events:

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

Client Events

Sometimes you may want to broadcast events to other connected clients without going through the Larravel application. This is especially useful when dealing with "input" notifications, such as telling the application user that other users are inputting information on a given screen. To broadcast client events, you can use Echo's whisper method:

 Echo.channel('chat') .whisper('typing', { name: this.user.name });

To listen to client events, you can use listenForWhisper method:

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

notice

Broadcast and notice JavaScript applications can receive new notifications without refreshing the current page when an event occurs. Before doing so, make sure you have read through Broadcast Notification Channel Document

After configuring the notification to use the radio channel, you can use Echo's notification Method to listen to broadcast events. Remember that the channel name should be consistent with the class name receiving notifications:

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

In this example, all broadcast Channel sent to App\User The instance notification will be received by this callback. Built in to the Larravel framework BroadcastServiceProvider Contains a App. User. {id} Channel authorization callback for channel.


give the thumbs-up Cancel Like Collection Cancel Collection

<<Previous: Implementation and use of Laravel queue system

>>Next: Build high-performance Larravel applications through cache