Skip to content

kanata-php/conveyor-laravel-broadcaster

Repository files navigation

Conveyor Laravel Broadcaster

This is a Laravel Integration for Socket Conveyor. It allows you to use the Conveyor WebSocket server as a broadcasting driver for Laravel. This package needs Jacked Server.

This package is an alternative for those who want to use Conveyor as a broadcasting driver. For that, you need to install Jacked Server or check there how to run your WebSocket server with Conveyor

Installation

Start by installing Jacked Server.

Step 1: Install the package via composer:

composer require kanata-php/conveyor-laravel-broadcaster

Step 2: Publish the configuration:

php artisan vendor:publish --provider="Kanata\LaravelBroadcaster\ConveyorServiceProvider"

Step 3: Add Service Provider to the config/app.php file:

<?php
return [
    // ...
    'providers' => [
        // ...
        Kanata\LaravelBroadcaster\ConveyorServiceProvider::class,
    ],
    // ...
];

Step 4: If on Laravel 11, enable Laravel broadcasting:

php artisan install:broadcasting

Step 5: Add the following to your config/broadcasting.php file:

<?php

return [
    // ...
    'conveyor' => [
        'driver' => 'conveyor',
    ],
];

Step 6: Protect your channel with a "channel route" (a specific laravel detail). You do this by adding the following to your routes/channels.php:

use App\Models\User;
use Illuminate\Support\Facades\Broadcast;

Broadcast::channel('actions-channel', function (User $user) {
    return true; // we are authorizing any user here - update according to your needs!
});

Step 7: This package require an user to authenticate with. To quickly create a user, you can use tinker for that:

php artisan tinker

Within tinker, you can create a user:

App\Models\User::factory()->create(['email' => '[email protected]', 'password' => Hash::make('password')]);

Step 8: Specify the configurations for the WebSocket server in the .env file:

Important: SQLite won't work well due to its lock mechanism and how concurrency happens with this service. It is recommended to use MySQL, Postgres, or a more robust database.

# ...
BROADCAST_CONNECTION=conveyor
# ...
# MySQL of Postgres are better alternatives that SQLite
CONVEYOR_DATABASE=pgsql
JACKED_SERVER_WEBSOCKET_ENABLED=true
# ...

At this point you can broadcast from your Laravel instance to the Conveyor WebSocket server. To understand how to broadcast with Laravel, visit Broadcasting.


Step 9: Install the Conveyor JS Client:

npm install socket-conveyor-client

Important: Don't forget to run npm run build!

Add this to the bootstrap.js file of your Laravel app so the Conveyor client is available globally:

import Conveyor from "socket-conveyor-client";

window.Conveyor = Conveyor;

Remember to run npm install and npm run dev or npm run prod to compile the assets.

Info: If you want to send one-off messages to the Conveyor WebSocket server, you can just dispatch an event like follows:

<?php

namespace App\Events;

use Illuminate\Broadcasting\InteractsWithBroadcasting;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;

class TestEvent implements ShouldBroadcastNow
{
    use InteractsWithBroadcasting;

    public function __construct(
        public string $message,
        public string $channel,
    ) {
        $this->broadcastVia('conveyor');
    }

    public function broadcastOn(): array
    {
        return [
            new PrivateChannel($this->channel),
        ];
    }
}
event(new App\Events\TestEvent( message: 'my message', channel: 'my-channel'));

Important: notice that we are using ShouldBroadcastNow instead of ShouldBroadcast. Conveyor doesn't need queueing and is much faster this way. If you want, you can still use queues.

Example of usage in a view with authorization at this point:

<html>
<head>
    <title>WS Client</title>
    @vite(['resources/css/app.css', 'resources/js/app.js'])
</head>
<body>

<textarea id="msg"></textarea>
<button id="btn-base">Base</button>
<button id="btn-broadcast">Broadcast</button>
<ul id="output"></ul>

<script type="text/javascript">
    // page elements
    const msg = document.getElementById('msg')
    const btnBase = document.getElementById('btn-base')
    const btnBroadcast = document.getElementById('btn-broadcast')
    const output = document.getElementById('output')

    const connect = (token) => {
        let conveyor = new window.Conveyor({
            protocol: '{{ $protocol }}',
            uri: '{{ $uri }}',
            port: {{ $wsPort }},
            channel: '{{ $channel }}',
            query: '?token=' + token,
            onMessage: (e) => output.innerHTML = e,
            onReady: () => {
                btnBase.addEventListener('click', () => conveyor.send(msg.value))
                btnBroadcast.addEventListener('click', () => conveyor.send(msg.value, 'broadcast-action'))
            },
        });
    };

    const  getAuth = (callback) => {
        fetch('/broadcasting/auth?channel_name={{ $channel }}', {
            headers: {
                'Accept': 'application/json',
            },
        })
            .then(response => response.json())
            .then(data => callback(data.auth))
            .catch(error => console.error(error));
    }

    document.addEventListener("DOMContentLoaded", () => getAuth(connect));
</script>
</body>
</html>

Then, add the route for this view at your routes/web.php file:

use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Auth;

Route::get('/ws-client', function () {
    Auth::loginUsingId(1); // here we authorize for the sake of the example.

    $protocol = config('jacked-server.ssl-enabled') ? 'wss' : 'ws';
    $port = config('jacked-server.ssl-enabled') ? config('jacked-server.ssl-port') : config('jacked-server.port');

    return view('ws-client', [
        'protocol' => $protocol,
        'uri' => '127.0.0.1',
        'wsPort' => $port,
        'channel' => 'private-my-channel',
    ]);
});