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.