Broadcasting system
Broadcasting system
- Introduction
- Concept Overview
- Define broadcast event
- Authorization channel
- ##Broadcast event
- Receive broadcast
- Presence Channel ##Client event
- Message notification
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 theIlluminate\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 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.
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:
You can use the Echo.private('chat')
.whisper('typing', {
name: this.user.name
});
method to listen for client events:Echo.private('chat')
.listenForWhisper('typing', (e) => {
console.log(e.name);
});
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