Skip to main content

Command Palette

Search for a command to run...

Socket in Laravel with Reverb

Updated
•3 min read•View as Markdown
Socket in Laravel with Reverb

Install Reverb in laravel

Install laravel echo in frontend with PusherJS

Update env

BROADCAST_DRIVER=reverb

REVERB_APP_ID=22..88
REVERB_APP_KEY=eql...x52m
REVERB_APP_SECRET=v3kdi...76dc
REVERB_HOST="sohag.pro"
REVERB_PORT=443
REVERB_SCHEME=https

VITE_REVERB_APP_KEY="${REVERB_APP_KEY}"
VITE_REVERB_HOST="${REVERB_HOST}"
VITE_REVERB_PORT="${REVERB_PORT}"
VITE_REVERB_SCHEME="${REVERB_SCHEME}"

Create Event

with ShouldBroadcast

<?php

namespace App\Events;

use App\Models\Car;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

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

    /**
     * Create a new event instance.
     */
    public function __construct(public Car $car)
    {
        //
    }

    /**
     * Get the channels the event should broadcast on.
     *
     * @return array<int, \Illuminate\Broadcasting\Channel>
     */
    public function broadcastOn(): array
    {
        return [
            new Channel('car.' . $this->car->id),
        ];
    }
}

Fire event


$car = Car::find(7);
TestEvent::dispatch($car);

Setup Frontend

in resources/js/bootstrap.js

import Echo from 'laravel-echo';

import Pusher from 'pusher-js';
window.Pusher = Pusher;

window.Echo = new Echo({
    broadcaster: 'reverb',
    key: import.meta.env.VITE_REVERB_APP_KEY,
    wsHost: import.meta.env.VITE_REVERB_HOST,
    wsPort: import.meta.env.VITE_REVERB_PORT,
    wssPort: import.meta.env.VITE_REVERB_PORT,
    forceTLS: (import.meta.env.VITE_REVERB_SCHEME ?? 'https') === 'https',
    enabledTransports: ['ws', 'wss'],
});

Receive Event in React

const [isSubscribed, setIsSubscribed] = useState(false);

useEffect(() => {
    if (!isSubscribed && car && car.id) {
        Echo.channel(`car.${car.id}`).listen("TestEvent", (e) => {
            console.log(e.car);
            setCar(e.car);
            setLatestBid(e.car.highest_bid.amount);
            toast.success("New Bid placed of $" + parseInt(e.car.highest_bid.amount));
        });
        setIsSubscribed(true); // Set subscribed flag to true
    }

    // Cleanup subscription on component unmount
    return () => {
        if (isSubscribed && car && car.id) {
            Echo.leave(`car.${car.id}`);
            setIsSubscribed(false); // Reset subscribed flag
        }
    };
}, [isSubscribed]);

Receive Event in Vanilla Js

const receiverName = '{{ $receiver->name }}';
const senderName = '{{ auth()->user()->name }}';
const receiverId = {{ $receiver->id }};
const senderId = {{ auth()->id() }};
const messagesDiv = document.getElementById('messages');
window.addEventListener('load', () => {
    window.Echo.private(`chat.{{ auth()->id() }}.{{ $receiver->id }}`)
        .listen('TestEvent', (e) => {
            console.log(e);
            let pushMessage = e.message.message;
            let image = e.message.image;
            let time = e.message.created_at;
            // format as 12 hour time format with date and time
            time = new Date(time).toLocaleString('en-US', {
                timeZone: '{{ config('app.timezone') }}',
                hour: 'numeric',
                minute: 'numeric',
                hour12: true,
                month: 'short',
                day: 'numeric'
            });
            document.getElementById('messages').innerHTML += generateBlock(pushMessage, image, receiverName,
                time);

            // messages div scroll to bottom
            messagesDiv.scrollTop = messagesDiv.scrollHeight;
        });
    window.Echo.private(`chat.{{ $receiver->id }}.{{ auth()->id() }}`)
        .listen('TestEvent', (e) => {
            console.log(e);
            let pushMessage = e.message.message;
            let image = e.message.image;
            let time = e.message.created_at;
            // format as 12 hour time format with date and time
            time = new Date(time).toLocaleString('en-US', {
                timeZone: '{{ config('app.timezone') }}',
                hour: 'numeric',
                minute: 'numeric',
                hour12: true,
                month: 'short',
                day: 'numeric'
            });
            document.getElementById('messages').innerHTML += generateBlock(pushMessage, image, senderName,
                time, true);

            // messages div scroll to bottom
            messagesDiv.scrollTop = messagesDiv.scrollHeight;
        });

    // messages div scroll to bottom
    messagesDiv.scrollTop = messagesDiv.scrollHeight;
})

Worker to keep reverb running

how to setup supervisor? https://notes.sohag.pro/supervisor-setup-for-laravel-application

Worker conf:

[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/html/laravel/artisan reverb:start
user=www-data
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
numprocs=1
redirect_stderr=true
stdout_logfile=/var/www/html/laravel/storage/logs/reverb.log
stopwaitsecs=3600

Nginx conf with support WSS

server {
        root /var/www/html/laravel/public;

        index index.php;
        server_name example.com;

        add_header X-Frame-Options "SAMEORIGIN";
        add_header X-XSS-Protection "1; mode=block";
        add_header X-Content-Type-Options "nosniff";


        charset utf-8;

        location / {
                try_files $uri $uri/ /index.php?$query_string;
            }

            location = /favicon.ico { access_log off; log_not_found off; }
            location = /robots.txt  { access_log off; log_not_found off; }

            error_page 404 /index.php;

            location ~ \.php$ {
                fastcgi_pass unix:/run/php/php8.2-fpm.sock;
                fastcgi_index index.php;
                fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
                include fastcgi_params;
            }

    location /app {
            proxy_http_version 1.1;
            proxy_set_header Host $http_host;
            proxy_set_header Scheme $scheme;
            proxy_set_header SERVER_PORT $server_port;
            proxy_set_header REMOTE_ADDR $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "Upgrade";

            proxy_pass http://0.0.0.0:8080;
        }

        location ~ /\.(?!well-known).* {
            deny all;
        }
}
A

Insightful

More from this blog

Sohag's Notes

100 posts

Tech Lead at MobyPay | AWS SAA Certified | Certified Laravel Developer | PHP • JS • Go | DevOps Enthusiast | Open to Projects