Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

webofink's avatar

Firstly, I'd like to apologise for the questions, I'm really very new to all of this.

How would I pass a variable to EventName.php from the route/controller?

For example, the route would be:

Route::get('/room/{name}', function ($name) {
    // this fires the event
    // how do I pass $name to:
    event(new App\Events\EventName());
    return "event fired";
});

And then, once I get the variable to EventName.php, how do I inject it into the function?

<?php namespace App\Events;

use App\Events\Event;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Queue\SerializesModels;

class EventName extends Event implements ShouldBroadcast
{
    use SerializesModels;

    public $data;
    //would public $name work here??

    public function __construct()
    {
        $this->data = array(
            'power'=> '10'
        );
    }

    public function broadcastOn()
    {
    // and then inject into this function, eg: return['room/'.$name]; ?
    }
}
graham's avatar

@webofink that's exactly what I was working out. Here's how I did it in the end:

The Route:

Route::get('fire/{msg}', function ($msg) {
    // this fires the event
    event(new App\Events\EventName($msg));
    return "new event fired";
});

EventName.php

<?php namespace App\Events;

use App\Events\Event;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Queue\SerializesModels;

class EventName extends Event implements ShouldBroadcast
{
    use SerializesModels;

    public $data;

    public function __construct($msg)
    {
        $this->data = array(
            'msg'=> $msg
        );
    }

    public function broadcastOn()
    {
        return ['test-channel'];
    }
}

test.blade.php

@extends('layouts.master')

@section('content')
    <p id="msg">0</p>
@stop

@section('footer')
    <script src="{{ asset('js/socket.io.js') }}"></script>
    <script>
        //var socket = io('http://localhost:3000');
        var socket = io('http://192.168.10.10:3000');
        socket.on("test-channel:App\\Events\\EventName", function(message){
            // increase the msg everytime we load test route
            $('#msg').text(message.data.msg);
        });
    </script>
    <script src="https://cdn.socket.io/socket.io-1.3.5.js"></script>
@stop
graham's avatar

@mstnorris works really well on Forge as others have mentioned.

For the benefit of anyone else trying to set this up on Forge:

Don't forget to open a port on the Firewall (Forge -> Server Details -> Network).

Add 2 new Daemons to your Forge server. For me they were:

redis-server --port 3001

node /home/forge/yourdomain.com/socket.js

1 like
webofink's avatar

@Graham thanks for that, it's very useful but unfortunately doesn't answer my question.

Imagine I have dynamic routes for rooms. For example /room/{id} could be /room/1, /room/2/, /room/140 whatever.

When I broadcast using /fire/1 for example, I only want to send to /room/1.

Currently it sends to /room/1, /room/2, /room/3, etc. Because by default here, everything on the server is subscribed to 'test-channel'. I just can't figure this out.

consigliere's avatar

@mstnorris

Can I do this?

var channels = ajaxGetChannels();

 redis.subscribe(channels, function(err, count) {
});

Is this a good way to do it if I'm doing dynamic channels?

ctaljaardt's avatar

Hello,

I have tried this and i have a problem

The error i get is : http://cl.ly/image/2p2A0a242H1W

I have the ports open on my firewall : http://cl.ly/image/2L1A2W2d3O3o

ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:3000

ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:3001

I have redis and node running as shown here :

30997:M 28 Jul 14:21:03.232 * Increased maximum number of open files to 10032 (it was originally set to 1024).
                _._                                                  
           _.-``__ ''-._                                             
      _.-``    `.  `_.  ''-._           Redis 3.0.3 (00000000/0) 64 bit
  .-`` .-```.  ```\/    _.,_ ''-._                                   
 (    '      ,       .-`  | `,    )     Running in standalone mode
 |`-._`-...-` __...-.``-._|'` _.-'|     Port: 3001
 |    `-._   `._    /     _.-'    |     PID: 30997
  `-._    `-._  `-./  _.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |           http://redis.io        
  `-._    `-._`-.__.-'_.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |                                  
  `-._    `-._`-.__.-'_.-'    _.-'                                   
      `-._    `-.__.-'    _.-'                                       
          `-._        _.-'                                           
              `-.__.-'                                               

30997:M 28 Jul 14:21:03.236 # Server started, Redis version 3.0.3
30997:M 28 Jul 14:21:03.236 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.
30997:M 28 Jul 14:21:03.236 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
30997:M 28 Jul 14:21:03.237 * The server is now ready to accept connections on port 3001

And Node :

Listening on Port 3000
Message Recieved: {"event":"App\\Events\\EventName","data":{"data":{"power":"10"}}}

Here is my views for the edit i done

test.blade.php

@extends('layouts.master')

@section('content')
    <p id="power">0</p>
@stop

@section('footer')
    <script src="https://cdn.socket.io/socket.io-1.3.5.js"></script>
    <script>
        //var socket = io('http://localhost:3000');
        var socket = io('173.xxx.xxx.228:3000');
        socket.on("test-channel:App\\Events\\EventName", function(message){
            // increase the power everytime we load test route
            $('#power').text(parseInt($('#power').text()) + parseInt(message.data.power));
        });
    </script>
@stop
graham's avatar

@ctaljaardt does it work if you temporarily disable the firewall in your Vesta control panel?

jonnywilliamson's avatar

Guys this is an excellent thread. I've had this working now for a good few of weeks. I would never have gotten it all to work with out the help in this guide, so many thanks to @mstnorris for putting this all together.

However I think there are a number of items that need to be clarified as I think there's some items in the instructions that don't make any sense to me. Then again I'm always open to correction.

As I have about 3 or 4 things to write, I'll break them down into separate posts. Will make it easier for people to respond too!

The first item - running the redis server:

The opening in the first post states:

In this walkthrough I'll assume you're using Laravel Homestead. Laravel Homestead already includes Node and Redis

So we know we have redis installed. We also set the broadcast driver to use redis.

Then the instructions state:

Starting the Servers In the first tab, run node socket.js In the second tab run redis-server --port 3001

Why would we run the redis-server? It's already working out of the box with homestead!

Also, you have the server running listening on port 3001....but at no stage does any code make a connection to that port to pass information to it.

I have been running this setup now for a few weeks. At no stage do I need to have a separate redis-server terminal window open for this all to work. All my push notifications work perfectly well without this (using https://redsmin.com/ I can see all the jobs/broadcasts arriving into my redis queue and being processed). Unless I am missing something, this appears to be a completely un-needed part of the instructions.

Perhaps someone could clarify.

jonnywilliamson's avatar

The next item is the socket.js file.

The original suggestion was to use this code:

var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var Redis = require('ioredis');
var redis = new Redis();
redis.subscribe('test-channel', function(err, count) {
});
redis.on('message', function(channel, message) {
    console.log('Message Recieved: ' + message);
    message = JSON.parse(message);
    io.emit(channel + ':' + message.event, message.data);
});
http.listen(3000, function(){
    console.log('Listening on Port 3000');
});

However, (perhaps this was written before Taylor got to rewrite all his documentation for 5.1) there seems to be a much better and more flexible script we can use directly out of the Laravel Docs.

If we take a look at the broadcasting docs, we can see that the following code allows more flexibility:

var app = require('http').createServer(handler);
var io = require('socket.io')(app);

var Redis = require('ioredis');
var redis = new Redis();

app.listen(6001, function() {
    console.log('Server is running!');
});

function handler(req, res) {
    res.writeHead(200);
    res.end('');
}

io.on('connection', function(socket) {
    //
});

redis.psubscribe('*', function(err, count) {
    //
});

redis.on('pmessage', function(subscribed, channel, message) {
    message = JSON.parse(message);
    io.emit(channel + ':' + message.event, message.data);
});

The main benefit is that we don't have to hard code any channel name into our socket.js file. With Taylor's new code the socket.io script can listen/subscribe on ANY channel. Less hard coding = more goodness!

I have been using Taylors new suggested code for weeks, and it's working perfectly.

As a side note for those who need to create a https node/socket.io server (the above code is only for a http server and you will have issues if you try to create a https node.js server and allow a non https connection to it) the following code works well.

var fs = require('fs');
// This line is from the Node.js HTTPS documentation.
var options = {
    key: fs.readFileSync('/etc/nginx/ssl/<makeyourownpath>/server.key'),
    cert: fs.readFileSync('/etc/nginx/ssl/<makeyourownpath>/server.crt')
};

var app = require('https').createServer(options, handler);
var io = require('socket.io')(app);

var Redis = require('ioredis');
var redis = new Redis();

app.listen(6001, function() {
    console.log('Server is running on port 6001!');
});

function handler(req, res) {
    res.writeHead(200);
    res.end('');
}

io.on('connection', function(socket) {
    //
});

redis.psubscribe('*', function(err, count) {
    //
});

redis.on('pmessage', function(subscribed, channel, message) {
    message = JSON.parse(message);
    io.emit(channel + ':' + message.event, message.data);
});
5 likes
jonnywilliamson's avatar

The next one is a small issue.

In the instructions it suggests to add the socket.io js file via a local asset

@section('footer')
    <script src="{ { asset('js/socket.io.js') } }"></script>
<snip>
@stop

Or also as per another comment a CDN link:

<script src="https://cdn.socket.io/socket.io-1.3.5.js"></script>

As per the excellent post by @REBELinBLUE here this can be served directly by the socket.io server using:

    {!! Html::script("/socket.io/socket.io.js") !!}

Or if you don't have the HTML package installed in laravel 5.1

    <script src="{ { asset('/socket.io/socket.io.js') } }"></script>

One less CDN in use is probably good news :)

jonnywilliamson's avatar

My final addition to this thread is the removal of the need to hard code the IP address in the JS section of your view when creating the socket.

The original suggests this:

@section('footer')
    <script>
        var socket = io('http://192.168.10.10:3000');
        socket.on("test-channel:App\\Events\\EventName", function(message){
            // increase the power everytime we load test route
            $('#power').text(parseInt($('#power').text()) + parseInt(message.data.power));
        });
    </script>
@stop 

I've found it's much easier with this:

        var socket = io(window.location.origin + ':6001');

This uses the current website address to connect to the node server, so now if I'm working on a development server (http://test.dev, or the actual server https://example.com) I don't need to edit this and change the actual IP address.

Hope it helps someone.

1 like
mstnorris's avatar

@jonnywilliamson thank you for your comments and corrections. I can't remember when I wrote this now whether it was pre/post 5.1 release.

I don't have any time right now, but I would love to include your suggestions into my original post. Feel free to send me the markdown of the four posts you added to mstnorris@gmail.com (that is of course if you want to).

Thanks again, -m

jonnywilliamson's avatar

@mstnorris . Sure I'll send some stuff over to you.

However I just realise I've left out the BIGGEST thing I initially came here to query with you all!!

QUEUES!

If we read the Broadcasting Laravel docs, we see it specifically states:

Queue Prerequisites Before broadcasting events, you will also need to configure and run a queue listener. All event broadcasting is done via queued jobs so that the response time of your application is not seriously affected.

So basically the work flow is this.

  1. User creates a normal Event class
  2. The event class must implement the Illuminate\Contracts\Broadcasting\ShouldBroadcast interface so Laravel knows this is a broadcast event.
  3. When a user fires the event, a job is created and placed on a queue.
  4. The actual queue is setup in the config file config/queue.php . It would be fair to suggest most of us are probably using the REDIS queue driver....or are we? (see below)
  5. The job will be placed on the queue named in the queue.php config file.
  6. A queue worker is needed to come along and take the job off the queue before sending it for broadcast.

But here's the issue...no-one (in this thread that I can see) has mentioned that they have actually had to have a queue worker running the background to process this Redis (or beanstalk or whatever) queue!!!

So how are you all getting this to work!?! You should all be having issues as far as I can see!

The only answer I have come up with is that most of you are using SYNC as your queue driver, and so the broadcast queue is being cleared instantly, but this just means that there isn't really a proper queue system in place.

On my own setup, I've had a queue worker daemon running to process my REDIS queue and actually process the broadcast jobs and actually make them transmit out. It's working great, but the lack of talk in here about queues has me completely perplexed!

For those interested, here's the command I'm using (that's being run under supervisor so that it doesn't crash and stop running)

php /home/forge/<mydirectory>/artisan queue:work redis --sleep=1 --daemon --quiet --tries=2 --queue="default"

Because I'm using a daemon, I need to ensure the following line is in my forge deploy script (at the end), so that any new code I push up is detected by the queue daemon:

php artisan queue:restart

Share the secret guys! I'm feeling very much in the dark! :)

6 likes
simtrax's avatar

@jonnywilliamson I'd love to hear an answer to you're question about queue's. I've followed the guide on page one, but can't get broadcasting to work. Tried both redis and pusher with no success.

Can someone shed some light on the subject?

jonnywilliamson's avatar

@simtrax - There's not much more people can write about! To be fair to the guide on page one it's fairly complete albeit with a few items I've added above for clarity.

The queue issue is an interesting one. If you have setup SYNC for your queue driver you shouldn't have any issues. Try doing that and see how you get on.

mstnorris's avatar

Apologies for not getting back to this thread as much as I'd have hoped. Will try to go through and add the amendments where appropriate soon.

simtrax's avatar

So i today found out that it was a problem with my cached config... So it seems to work fine now. I'll keep on working on it.

willvincent's avatar

I don't know if it's been mentioned already, this is a long thread... but you could completely skip redis and just talk directly to the socket server with a php socket library like Ratchet, or Elephant.io .. less server side requirements, less moving parts, etc.

jonnywilliamson's avatar

So no-one has gotten back to me about how they are using this without setting up a queue worker etc (unless of course you're all using SYNC).

Anyway, Taylor merged a PR from me earlier that allows us to specify the name of the queue that the broadcast jobs are sent to. Why would you do this? Well, if the default queue still has previous jobs on it, all of those jobs have to be completed before any broadcast events can be processed!

By specifying a queue name, we can send all the broadcast jobs to their own queue and so a worker can process them exclusively making it all work much better.

To specify a queue name for a broadcast job all you have to do is open your broadcast event job and add a method called onQueue() that returns the queue name as a string like this:

// demoBroadcastEvent.php

public function onQueue() {
    return 'exclusivequeue'
}
2 likes
ikeras's avatar

Hi, I have similar setup, but would like to broadcast to everyone in the channel EXCEPT the sender. I'm using calendar app and it duplicates events on senders page.

Is there a way to use socket.broadcast.emit() or should I include some id in the event and manually check for it in the client side?

Manzoor-Bajwa's avatar

@mstnorris , @jonnywilliamson I need uni-cast instead of broadcast. Currently its broadcasting to all clients connected to a socket. I want to send messages to specific clients only. Please help me.

Hebilicious's avatar

This thread is awesome! If anyone want to try doing that, there's a really good lesson on Pusher in the Laravel 5.1 series. It's quite simple to get the same results with socket.io/redis.

Some tips, I hope someone will find them useful :

  • As pointed out by @jonnywilliamson, the code in the official doc is more flexible, as it allows "on the fly" channel creation, so you don't have to change the node server configuration when you want to add a channel.

  • When you do it like that, you can pass an array in the broadcastOn method like :

public function broadcastOn()
{
    return ['firstchannel', 'secondchannel', 'testchannel'];
}

And socket.io will broadcast the message on each channel simultaneously, which is really cool and powerful. (You can test that in the node server configuration:

console.log('Message ' + message + ' broadcasted on ' + channel);
  • Once everything has been set up correctly, instead of running the server with node, you can use pm2, which is perfect and as easy as :
pm2 start socket.js 
  • If you're app is served on a non standard port (like http://myapp.com:8080), you can use that instead in your client side javascript (you still have to 'hardcode' the socket.io port number):
var socket = io(window.location.hostname+ ':3000'); 
2 likes
bugsysha's avatar

It's great having people like you that are willing to share their knowledge. Thanks a million.

1 like
pdeschamps's avatar

Can you do a guid like the one above on implementing caching with redis on a laravel 5.1 app?

davorminchorov's avatar

I know that this question is not related to the guide but what does redis do in all this? Are there any alternatives (except paid services like Pusher)?

punchi's avatar

Thanks for the tutorial :D but... how do I send the information to a specific user? let's say I have a notification bar and I have 10 users, every user have their own notification!! how can I send the notifications to the respective users? @_@

Please or to participate in this conversation.