Laravel + WebSockets

Photo by Agata Kędziorek on Unsplash

Photo by Agata Kędziorek on Unsplash

By enabling your Web Application to broadcast messages to the clients' web browser using WebSockets you can make your app feel just as responsive as an offline app while adding amazing real-time features such as seeing what other users do while they do it and having the backend code send a message to the browser once it's finished processing some data.

In this guide, I will list the basic building blocks to have you up and running in no time to build your own real-time applications using Laravel. This guide does expect the reader to be familiar with the basics of Laravel, and will only cover WebSocket-specific setup and configuration.

Pusher

Get credentials for WS server

Laravel has a great packet to enable Pusher-compliant WebSocket server connections. To get started you can either run your own WebSocket server like the one explained in the article below, or you can register an account on pusher.com.

Once you have credentials to connect both the front-end and back-end to the pusher-compliant server you can proceed.

Environment files

Adding your credentials

To your .env file you can now add the pusher related information.

vim .env
.env
...
BROADCAST_DRIVER=pusher
...

...
PUSHER_APP_ID=1337
PUSHER_APP_KEY=frontEndKeyYouGot
PUSHER_APP_SECRET=backEndKeyYouGot
PUSHER_HOST=ws.haxor.no
PUSHER_PORT=443
PUSHER_SCHEME=https
PUSHER_APP_CLUSTER=C2

MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
MIX_PUSHER_HOST="${PUSHER_HOST}"
MIX_PUSHER_PORT="${PUSHER_PORT}"
MIX_PUSHER_SCHEME="${PUSHER_SCHEME}"
...

Please make note of the BROADCAST_DRIVER variable. By default, this is set to "log". By being set to log, the broadcast messages will only be sent to your Laravel log file, and not to the pusher-compliant WebSockets server!

Backend Setup

Install stuff to make magic

To make Laravel able to communicate with the WebSocket server the "pusher-php-server" package has to be installed using composer.

composer require pusher/pusher-php-server

Once this is installed the BrodcastServiceProvider class has to be uncommented in the application configuration, to enable the use of the pusher-server.

vim config/app.php
config/app.php
...
App\Providers\BroadcastServiceProvider::class,
...

Optionally: If you want to make use of your own WebSocket server you must change a couple of variables in the pusher config.

vim config/broadcasting.php
config/broadcasting.php
...
'pusher' => [
      'driver' => 'pusher',
      'key' => env('PUSHER_APP_KEY'),
      'secret' => env('PUSHER_APP_SECRET'),
      'app_id' => env('PUSHER_APP_ID'),
      'options' => [
          'host' => env('PUSHER_HOST'),
          'port' => env('PUSHER_PORT', 443),
          'scheme' => env('PUSHER_SCHEME', 'https'),
          'encrypted' => true,
          'useTLS' => env('PUSHER_SCHEME', 'https') === 'https',
      ],
      'client_options' => [
          // Guzzle client options: https://docs.guzzlephp.org/en/stable/request-options.html
      ],
  ],
...

Frontend Setup

To make your frontend application able to receive messages from the WebSocket server you will need to install laravel-echo and pusher-js npm packages.

npm install --save-dev laravel-echo pusher-js

Finally, you must uncomment the WebSocket-related stuff found in the bootstrap JavaScript file. I prefer using Laravel-mix, so the file dump below will correspond to my .env files:

vim /resources/js/bootstrap.js
/resources/js/bootstrap.js
...
import Echo from 'laravel-echo';
import Pusher from 'pusher-js';
window.Pusher = Pusher;

window.Echo = new Echo({
    broadcaster: 'pusher',
    key: process.env.MIX_PUSHER_APP_KEY,
    cluster: process.env.MIX_PUSHER_APP_CLUSTER,
    wsHost: process.env.MIX_PUSHER_HOST,
    wsPort: process.env.MIX_PUSHER_PORT,
    encrypted: process.env.MIX_PUSHER_SCHEME == 'https',
    forceTLS: process.env.MIX_PUSHER_SCHEME == 'https',
    disableStats: true
});
...

Now the basic building blocks are in place to start building your real-time WebSocket functionality.

Broadcast Events

Backend code that bradcasts to the WebSocket server.

When you want to bradcast from your application to the client web browser you use Laravel "Events", that implements the ShouldBroadcast class.

Simply put you define public variable that gets filled out once the event gets created. The Events gets put into an event queue. And once it gets processed by Laravel it will broadcast all those public variables to the WebSocket server.

An event can be created using Laravel artisan

php -f artisan make:event TestBroadcast

this will create a new file in your app directory. Open it and add basic class features to make it work as a broadcast event.

vim app/event/TestBroadcast.php
app/event/TestBroadcast.php
<?php

namespace App\Events;

use Illuminate\Support\Facades\Log;
use Illuminate\Queue\SerializesModels;
use Illuminate\Broadcasting\Channel;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;

class TestBroadcast implements ShouldBroadcast
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public $testData;

    /**
     * Create a new event instance.
     *
     * @return void
     */
    public function __construct($testData)
    {
        $this->testData= $testData;
    }

    /**
     * Get the channels the event should broadcast on.
     *
     * @return \Illuminate\Broadcasting\Channel|array
     */
    public function broadcastOn()
    {
        return new Channel("testchannel");
    }
}

Please note that a "Channel" is a public channel that requires no authentication to listen on. To require authentication before connecting to a channel you must use a Private channel.

To make use of this event you can broadcast it in any controller using this line of code

app/Http/Controllers/TestController.php
...
use App\Events\TestBroadcast;
...

...
broadcast(new TestBroadcast("testing testing - 123"));
...

Now the Laravel application is broadcasting messages using the broadcast event you created.

Listen for messages

Listen on channels for events in the frontend app.

To catch the broadcasted message you simply use laravel-echo to listen on the public channel and the specific event. Thankfully the event has the exact same name as the Broadcast event class you created.

app.js
...
window.Echo.channel('testchannel')
    .listen('TestBroadcast', (event) => {
        console.log(event);
    })
...

If you have multiple broadcast events on the same channel, you can simply chain ".listen" methods.

Congratulations! 🎉 You now have a fully functioning WebSocket project running on Laravel! Unleash your creativity to build amazing applications that will make the world a slightly better place.

Private channels

Require authentication to listen om broadcasts

There are times when you need to require your users to authenticate to be able to listen to messages on a channel. To do this you simply use the PrivateChannel class in your event, add it as an entry in your channel routes, and use the private channel method with laravel-echo.

The PHP broadcast event will look like this.

app/event/TestBroadcast.php
...
use Illuminate\Broadcasting\PrivateChannel;
...

...
public function broadcastOn()
    {
        return new PrivateChannel("testchannel");
    }
...

Your channels route will look like this:

routes/channels.php
...
Broadcast::channel('testchannel', function($user) {
    return $user != null;
});
...

You can get really creative with your channel route file. In this file, you can set requirements far more complex that a simple check if there is a user session established.

Lastly, your JavaScript will look like this:

app.js
...
window.Echo.private('testchannel')
    .listen('TaskUpdated', (event) => {
        console.log(event);
    });
...

You now have all the basic building blocks to enable you to build complex real-time apps using Laravel and WebSockets.