Today, let’s take a closer look at Laravel’s broadcast system. The purpose of the broadcast system is to realize the function of pushing messages to the client when the server completes a certain function. In this article we will learn how to use the functionality of third-party Pusher tools to push messages to clients.
If you need to implement functions in Laravel such as sending a message to a client when the server processes something, you need to use Laravel’s broadcast system.
For example, in an IM application that allows users to send messages to each other, when user A sends A message to user B, the system pushes the message to user B in real time, and the message is displayed to user B in A pop-up box or prompt message box.
This scenario perfectly illustrates how the Laravel broadcast system works. In addition, this tutorial will implement such an instant messaging application using the Laravel broadcast system.
You may be interested in the technical principles of how the server pushes messages to the client in a timely manner because of the socket programming techniques used to implement this function on the server side. Before we start implementing an im system, let’s take a look at the general flow of socket programming:
- First, the server needs to support the WebSocket protocol and allow clients to establish WebSocket connections.
- You can implement your own WebSocket service or use a third-party service such as Pusher, which uses the Pusher library later;
- The client creates a Web Socket connection for the server. After the connection is successful, the client obtains a unique identifier.
- Once the client is connected successfully, it indicates that the client has subscribed to the specified channel and will receive the message of this channel.
- Finally, clients register listening events for the channels they subscribe to.
- When the server completes the specified function, we notify the WebSocket server with the specified channel name and event name.
- Eventually, the WebSocket server pushes the specified event as broadcast to all clients that sign up to listen on the channel.
This may seem like a lot, but you’ll get the hang of it in this article.
Next, let’s open the Laravel default broadcast system configuration file config/broadcasting.php to see the configuration options in it:
<? PHP return [/ * | -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - | the Default Default radio Broadcaster |-------------------------------------------------------------------------- | | This option controls the default broadcaster that will be used by the | framework when an event needs to be broadcast. You may set this to | any of the Connections defined in the "connections" array below. | | this configuration option to configure projects need to provide broadcasting service to drive. Configure connector can make any | in "connections" node configuration driver name. | | Supported: "Pusher", the "redis", "log", "null" | | support: "pusher", "redis", "log", "null" | */ 'default' => env('BROADCAST_DRIVER', 'null'), /* |-------------------------------------------------------------------------- | Broadcast Connections |-------------------------------------------------------------------------- | | Here you may define all of the broadcast connections that will be used | to broadcast events to other systems or over websockets. Samples of | each available type of connection are provided inside this array. | */ 'connections' => [ 'pusher' => [ 'driver' => 'pusher', 'key' => env('PUSHER_APP_KEY'), 'secret' => env('PUSHER_APP_SECRET'), 'app_id' => env('PUSHER_APP_ID'), 'options' => [ 'cluster' => env('PUSHER_APP_CLUSTER'), 'encrypted' => true, ], ], 'redis' => [ 'driver' => 'redis', 'connection' => 'default', ], 'log' => [ 'driver' => 'log', ], 'null' => [ 'driver' => 'null', ], ], ];Copy the code
The Laravel framework provides a number of broadcast driver programs out of the box by default.
This article uses Pusher as the broadcast driver. However, during the debugging phase, we can choose to use log as the broadcast driver. If the log driver is used, the client will not receive any messages and will simply write the messages that need to be broadcast to the laravel.log log file.
In the next section, we’ll take a closer look at how to implement an instant messaging application.
preparation
The Laravel broadcast system supports three different channel types – public, private and Presence. When the system needs to push information to the user, it can use the “public” channel. Conversely, if you only need to push messages to a specified channel, you need to use a channel of the “private” type.
Our example project will implement a messaging system that allows only logged-in users to receive instant messages, so a “private” type of channel will be used.
Out-of-the-box authentication services
First of all, for the newly created Laravel project, we need to install the out-of-the-box authentication service component provided by Laravel. The default authentication service functions include: registration, login and other functions. If you don’t know how to use the default authentication service, check out Laravel’s user Authentication System documentation for a quick start.
Server Pusher SDK installation configuration
Here we will use the third-party service Pusher as the WebSocket server, so we will also need to create an account and ensure that we have obtained the API certificate. If you encounter any problems with the setup, please comment in the comments section.
You then need to install the PHP version SDK of Pusher using the Composer package management tool so that you can send broadcast messages using Pusher in the Laravel project.
Now go to the root directory of the Laravel project and execute the following command to install:
Composer require pusher/pusher - PHP - server "to 3.0"Copy the code
After the installation is complete, modify the broadcast configuration file to enable the Pusher driver as the driver of the broadcast system.
<? php return [ /* |-------------------------------------------------------------------------- | Default Broadcaster |-------------------------------------------------------------------------- | | This option controls the default broadcaster that will be used by the | framework when an event needs to be broadcast. You may set this to | any of the connections defined in the "connections" array below. | | Supported: "pusher", "redis", "log", "null" | */ 'default' => env('BROADCAST_DRIVER', 'pusher'), /* |-------------------------------------------------------------------------- | Broadcast Connections |-------------------------------------------------------------------------- | | Here you may define all of the broadcast connections that will be used | to broadcast events to other systems or over websockets. Samples of | each available type of connection are provided inside this array. | */ 'connections' => [ 'pusher' => [ 'driver' => 'pusher', 'key' => env('PUSHER_APP_KEY'), 'secret' => env('PUSHER_APP_SECRET'), 'app_id' => env('PUSHER_APP_ID'), 'options' => [ 'cluster' => 'ap2', 'encrypted' => true ], ], 'redis' => [ 'driver' => 'redis', 'connection' => 'default', ], 'log' => [ 'driver' => 'log', ], 'null' => [ 'driver' => 'null', ], ], ];Copy the code
As you can see, we modified the default drive. And change the Pusher configuration cluster of connections nodes to AP2.
There are also configuration options that need to be retrieved from the.env configuration file, so we need to update the.env file to include the following configuration information:
BROADCAST_DRIVER=pusher
PUSHER_APP_ID={YOUR_APP_ID}
PUSHER_APP_KEY={YOUR_APP_KEY}
PUSHER_APP_SECRET={YOUR_APP_SECRET}Copy the code
Next, you need to make a few changes to the Laravel core file to use the latest Pusher SDK. However, I don’t advocate changing the Laravel core file, so I changed the code for the sake of the demonstration.
Let’s open the vendor/laravel/framework/SRC/Illuminate / – / Broadcasters/PusherBroadcaster. PHP file. Will use Pusher; Use Pusher\Pusher; .
After opening the vendor/laravel/framework/SRC/Illuminate / – / BroadcastManager. PHP file, do the same in the similar to the following code changes:
return new PusherBroadcaster(
new \Pusher\Pusher($config['key'], $config['secret'],
$config['app_id'], Arr::get($config, 'options', []))
);Copy the code
Finally, enable the broadcast service provider configuration in config/app.php configuration:
App\Providers\BroadcastServiceProvider::class,Copy the code
This completes the installation of the Pusher library. In the next section, we’ll cover the installation of the client class library.
Install and configure the client Pusher and Laravel Echo libraries
In a broadcast system, the client interface is responsible for connecting to the WebSocket server, subscribing to specified channels, and listening for events.
Fortunately, Laravel already provides us with a plugin called Laravel Echo, which implements a complex JavaScript client program. And the plug-in has built-in support for Pusher’s server connection.
The Laravel Echo module can be installed through the NPM package manager. If you haven’t already installed Node.js and the NPM package manager, install Node.js first.
Here I assume you have Node.js installed, so the command to install the Laravel Echo extension is as follows:
npm install laravel-echoCopy the code
Node_modules /laravel-echo/dist/echo. Js to public/echo. Js.
Using only one echo.js file is a bit of an overkill, so you can also download the echo.js file from Github.
At this point, we are finished installing the client component.
Server file Settings
Recall from the previous section: first we needed to implement an application that allowed users to send messages to each other; In addition, the application pushes messages through the broadcast system to users who have logged in to the system and have received messages.
In this section we will write server-side code to implement broadcast system functions.
Create the message migration file
First, we need to create a Message model to store messages sent by users. Create a migration file by executing the following command:
php make:model Message --migrationCopy the code
However, before executing the migrate command, we need to add the table fields to, from, and message to the migrated file.
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateMessagesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('messages', function (Blueprint $table) {
$table->increments('id');
$table->integer('from', false, true);
$table->integer('to', false, true);
$table->text('message');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('messages');
}
}Copy the code
Then run the migrate command to run the database migration files:
php artisan migrateCopy the code
When we need to execute events in Laravel, the first thing we need to do is create an event class, and Laravel will perform different actions based on different event types.
If the event is a normal event, Laravel calls the corresponding listener class. If the event type is a broadcast event, Laravel pushes the event to the WebSocket server using a driver configured in config/broadcasting.
This article uses the Pusher service, so Laravel pushes events to the Pusher server.
Create an event class using the following artisan command:
php artisan make:event NewMessageNotificationCopy the code
This command will create app/Events/NewMessageNotification PHP file, let’s modify the file within the code:
<?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\ShouldBroadcastNow;
use App\Message;
class NewMessageNotification implements ShouldBroadcastNow
{
use SerializesModels;
public $message;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct(Message $message)
{
$this->message = $message;
}
/**
* Get the channels the event should broadcast on.
*
* @return Channel|array
*/
public function broadcastOn()
{
return new PrivateChannel('user.'.$this->message->to);
}
}Copy the code
It’s important to note that the NewMessageNotification class implements ShouldBroadcastNow, so when we trigger an event, Laravel will immediately know that there is an event that needs to be broadcast to other users.
In fact, we can also implement the ShouldBroadcast interface, which queues events to messages. The queue Worker processes then execute the queue in sequence according to the queue entry order. Since our project needs to push messages to users immediately, it’s more appropriate to implement ShouldBroadcastNow.
Also, we need to display the Message information received by the user, so we take the Message model as an argument to the constructor so that the Message information is passed along with the event to the specified channel.
You then create a broadcastOn method in the NewMessageNotification class where you define the channel name for the broadcast event, because only the logged in user can receive the message, so you create the PrivateChannel instance as a PrivateChannel.
$this->message->to define the channel name in a format similar to user.{USER_ID}, which contains the user ID pointing to the receiving message.
The client program needs to verify the user’s identity before connecting to the WebSocket server. This ensures that messages from private channels are broadcast only to logged-in users. On the client side, only logged-in users are allowed to subscribe to user.{USER_ID} private channels.
If you use the Laravel Echo component on the client side to handle the subscription service. That’s just setting up channel routing in the client code, without worrying about user authentication processing details.
Open the routes/channels.php file and define a broadcast route:
<? php /* |-------------------------------------------------------------------------- | Broadcast Channels |-------------------------------------------------------------------------- | | Here you may register all of the event broadcasting channels that your | application supports. The given channel authorization callbacks are | used to check if an authenticated user can listen to the channel. | */ Broadcast::channel('App.User.{id}', function ($user, $id) { return (int) $user->id === (int) $id; }); Broadcast::channel('user.{toUserId}', function ($user, $toUserId) { return $user->id == $toUserId; });Copy the code
{toUserId}. The second argument to the Broadcast::channel method receives a closure, and Laravel automatically injects the logged-in user information into the first argument of the closure. The second argument is parsed and retrieved from the channel.
When a client tries to subscribe to the private channel user.{USER_ID}, the Laravel Echo component uses XMLHttpRequest as an asynchronous request for user authentication.
At this point im all the coding is done.
Create test cases
First, create a controller Http/app/Controllers/MessageController PHP:
<? php namespace App\Http\Controllers; use App\Http\Controllers\Controller; use App\Message; use App\Events\NewMessageNotification; use Illuminate\Support\Facades\Auth; class MessageController extends Controller { public function __construct() { $this->middleware('auth'); } public function index() { $user_id = Auth::user()->id; $data = array('user_id' => $user_id); return view('broadcast', $data); } public function send() { // ... $message = new message; $message->setAttribute('from', 1); $message->setAttribute('to', 2); $message->setAttribute('message', 'Demo message from user 1 to user 2'); $message->save(); // Add NewMessageNotification to event(new NewMessageNotification($message)); / /... }}Copy the code
Next the broadcast view files needed to create the index routing resources/views/broadcast blade. PHP:
<! DOCTYPE html> <html lang="{{ app()->getLocale() }}"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <! -- CSRF Token --> <meta name="csrf-token" content="{{ csrf_token() }}"> <title>Test</title> <! -- Styles --> <link href="{{ asset('css/app.css') }}" rel="stylesheet"> </head> <body> <div id="app"> <nav class="navbar navbar-default navbar-static-top"> <div class="container"> <div class="navbar-header"> <! -- Collapsed Hamburger --> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#app-navbar-collapse"> <span class="sr-only">Toggle Navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <! -- Branding Image --> <a class="navbar-brand123" href="{{ url('/') }}"> Test </a> </div> <div class="collapse navbar-collapse" id="app-navbar-collapse"> <! -- Left Side Of Navbar --> <ul class="nav navbar-nav"> </ul> <! -- Right Side Of Navbar --> <ul class="nav navbar-nav navbar-right"> <! -- Authentication Links --> @if (Auth::guest()) <li><a href="{{ route('login') }}">Login</a></li> <li><a href="{{ route('register') }}">Register</a></li> @else <li class="dropdown"> <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"> {{ Auth::user()->name }} <span class="caret"></span> </a> <ul class="dropdown-menu" role="menu"> <li> <a href="{{ route('logout') }}" onclick="event.preventDefault(); document.getElementById('logout-form').submit();" > Logout </a> <form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;" > {{ csrf_field() }} </form> </li> </ul> </li> @endif </ul> </div> </div> </nav> <div class="content"> <div class="m-b-md"> New notification will be alerted realtime! </div> </div> </div> <! -- receive notifications --> <script src="{{ asset('js/echo.js') }}"></script> <script SRC = "https://js.pusher.com/4.1/pusher.min.js" > < / script > < script > Pusher. LogToConsole = true; window.Echo = new Echo({ broadcaster: 'pusher', key: 'c91c1b7e8c6ece46053b', cluster: 'ap2', encrypted: true, logToConsole: true }); Echo.private('user.{{ $user_id }}') .listen('NewMessageNotification', (e) => { alert(e.message.message); }); </script> <! -- receive notifications --> </body> </html>Copy the code
After that, open the routes/web.php routing configuration file to define the HTTP route:
Route::get('message/index', 'MessageController@index');
Route::get('message/send', 'MessageController@send');Copy the code
The auth middleware used in the MessageController constructor ensures that only logged-in users can access the above routes.
Next, let’s examine the core code of the Broadcast view file:
<! -- receive notifications --> <script src="{{ asset('js/echo.js') }}"></script> <script SRC = "https://js.pusher.com/4.1/pusher.min.js" > < / script > < script > Pusher. LogToConsole = true; window.Echo = new Echo({ broadcaster: 'pusher', key: 'c91c1b7e8c6ece46053b', cluster: 'ap2', encrypted: true, logToConsole: true }); Echo.private('user.{{ $user_id }}') .listen('NewMessageNotification', (e) => { alert(e.message.message); }); </script> <! -- receive notifications -->Copy the code
Js and pusher.min.js are the necessary modules to connect to the pusher server using Laravel Echo.
Next, create the Laravel Echo instance.
After that, the private channel user.{USER_ID} is subscribed to via the private method of the Echo instance. Previously we said that only logged-in users can subscribe to private channels, so Echo instances use XHR to verify users asynchronously. Laravel then tries to find the user.{USER_ID} route and matches the broadcast route defined in the routes/channels.php file.
If all goes well, our project will have completed the Pusher server connection at this point and will then listen for the User. {USER_ID} channel. In this way, the client can normally receive all messages for the specified channel.
After the client receives WebSocket server messages, the server needs to send a broadcast Message through the Message::send method.
The code sent is as follows:
public function send() { // ... $message = new message; $message->setAttribute('from', 1); $message->setAttribute('to', 2); $message->setAttribute('message', 'Demo message from user 1 to user 2'); $message->save(); // Add NewMessageNotification to event(new NewMessageNotification($message)); / /... }Copy the code
This code first simulates sending a message by a logged-in user.
The NewMessageNotification event class instance is then added to the broadcast channel via the Event helper function. Since NewMessageNotification is an instance of the ShouldBroadcastNow class, Laravel reads the broadcast configuration data from the config/broadcasting.php configuration file, The NewMessageNotification event is then distributed to the User. {USER_ID} channel of the WebSocket server configured in the configuration file.
For this article the example will broadcast the message to the User. {USER_ID} channel of the Pusher server. If the subscriber ID is 1, the event is on the broadcast channel user.1.
Before, we have completed channel subscription and monitoring in the front-end code. Here, when users receive messages, a message box will pop up on the page to remind users.
How do you test the above functionality now?
Visit http://your-laravel-site-domain/message/index in your browser. If you have not logged in to the system, log in first, and then you can see the broadcast page.
Although the Current Web page appears to be doing nothing, Laravel has done a lot of processing in the background. Debugging of Pusher can be enabled through the pusher. logToConsole of the Pusher component. The following is the debugging information after login:
Pusher : State changed : initialized -> connecting Pusher : Connecting : {" transport ":" ws ", "url" : "wss://ws-ap2.pusher.com: 443 / app/c91c1b7e8c6ece46053b? Protocol = 7 & client = js&version = 4.1.0 & flash = f alse"} Pusher : Connecting : {"transport":"xhr_streaming","url":"https://sockjs-ap2.pusher.com:443/pusher/app/c91c1b7e8c6ece46053b?protocol=7&client= Js&version =4.1.0"} Pusher: State changed: connecting -> Connected with new socket ID 1386.68660 Pusher: Event sent: {"event":"pusher:subscribe","data":{"auth":"c91c1b7e8c6ece46053b:cd8b924580e2cbbd2977fd4ef0d41f1846eb358e9b7c327d89ff6bd c2de9082d","channel":"private-user.2"}} Pusher : Event recd : {"event":"pusher_internal:subscription_succeeded","data":{},"channel":"private-user.2"} Pusher : No callbacks on private-user.2 for pusher:subscription_succeededCopy the code
You can see that we have completed the WebSocket server connection and private channel listening. Of course, the channel name you see is not the same as mine, but the content is roughly the same. Next, do not close the Web page, and then go to the send method to send the message.
Open a new page window and visit http://your-laravel-site-domain/message/send in your browser. If successful, you will receive a prompt message on the http://your-laravel-site-domain/message/index page.
In the Index console you will also see the following debugging information:
Pusher : Event recd : {"event":"App\\Events\\NewMessageNotification","data":{"message":{"id":57,"from":1,"to":2,"message":"Demo message from user 1 to user 2","created_at":"2018-01-13 07:10:10","updated_at":"2018-01-13 07:10:10"}},"channel":"private-user.2"}Copy the code
As you can see, the debug message tells us that we receive App\Events\NewMessageNotification messages from the private-user.2 channel of the Pusher server.
Of course, this message can also be seen in the dashboard of the Pusher administration background, on the Debug Console TAB, where we can see the following log message.
That’s all for today, I hope you can help.
conclusion
Today, we examine the lesser-used feature of Laravel’s broadcast. Broadcast allows us to send real-time messages using Web Sockets. We also implemented a simple real-time notification push project using the broadcast feature. This article is a lot of content, need some time to digest, any questions can feel free to contact me.
The original