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

mstnorris's avatar

@lstables if you follow the guide, you will get it all up and running. From there you can send notifications to everyone listening on that channel. Give it a try :)

theUnforgiven's avatar

@mstnorris followed the guide and got it working as per example so refreshing increases 10 every time like you shown, but how would I pass other data? Do i pass like normal from controller to view, from repo to controller that sort thing?

theUnforgiven's avatar

Would I be better of doing:

protected $listen = [
    'App\Events\PodcastWasPurchased' => [
        'App\Handlers\Events\EmailPurchaseConfirmation',
    ],
];

Then running event:generate but then how would I make this work, what calls do I need to make what data can I pass, or am I just over complicating matters here!

mstnorris's avatar

@lstables I think you're over complicating it a little, depends on what you need. First start off small; get it working, you can always scale up if need be.

mstnorris's avatar

Yes as long as you're sure what you're passing is what you want to pass. For example, if everyone has access to it, ensure that there isn't any private data, but yeah, in short, you can do what you like.

I'm going to come back and revisit this topic and detail my progress when installing it to a DO box though Forge, I might then tinker around with it and flesh out the example into something more real-world as this is a very basic "get you started" guide (at least it is complete unlike some out there that miss crucial bits here and there).

2 likes
theUnforgiven's avatar

That would be good!

In terms of passing stuff I need to just say when a edit as taken place, then email everyone involved and display a message on screen too.

theUnforgiven's avatar

Tried passing the following through.

 public function sendNotificationAboutUpdate($client)
    {
        event(new Notifications($client));
    }

Which comes from (in my controllers update method)

$client = Client::find($id);
$this->sendNotificationAboutUpdate($client);

But then getting Undefined variable for things like $client etc

mstnorris's avatar

@lstables just be careful when you say email everyone as that could very very quickly become huge!

You may want to think about deferring to an "Here's what happened today" kind of approach, anyway, that's just some food for thought.

mstnorris's avatar

@edstevo yes the post is linked to in the guide is where I got inspiration for this one but there were too many things missing so I decided to try and follow along and make notes as I went on where I needed help. I thought others (like yourself) would benefit from it (I'm glad you did!) so I put it up here.

NicolasParada's avatar

Thanks for the guide :D Worked fine. I tried this before, and nothing :( It was because I never started the redis server D:

1 like
edstevo's avatar

@mstnorris - not sure how far you've got with your integration with Forge.

I replicated the steps on your tutorial here and it now works, but I did have a few issues with Digital Ocean / Forge.

  1. The first problem I had was installing nodejs on the droplet This was solved with this tutorial: https://www.digitalocean.com/community/tutorials/how-to-install-node-js-on-an-ubuntu-14-04-server

  2. The second problem was that by default Digital Ocean closes ports. So socket.io was timing out on port 3000. This was solved using this tutorial: https://www.digitalocean.com/community/questions/socket-io-connection-timing-out

Thanks again, and I hope this helps.

3 likes
mstnorris's avatar

@edstevo I haven't started with Forge yet so I will be sure to check out those two resources you mentioned. Thank you.

ryank30's avatar

@mstnorris Thanks for sharing a great tutorial. I have followed it on my dedicated server. However I have failed to see the results you posted. One thing that confused me was that you said that two different cmd need to be opened. I downloaded mtputty and opened two tabs and ran node socket.js on one tab and ran redis-server --port 3001. Is this correct thing to do or you meant something else?

When I checked the console on /test page, the response was null. I don't know what went wrong. It would be appreciated if you can help me out.

lihaibh's avatar

"In the second tab run redis-server --port 3001" If im working locally (not on Homestead) how can i run redis-server command?

omurphy's avatar

Awesome tutorial. I can confirm that it does work on a default Laravel Forge setup with Digital Ocean.

Here were the problems I encountered in case anyone else encounters the same thing:

  1. On my dev computer I was having the socket listen to localhost at port 3000. When I took everything live I had to switch it to listen to my domain.com instead of localhost (or http://127.0.0.1:3000/). I had to change this on both the server side socket/node.js file and the client side.
    note: you might also need to manually open port 3000 on digital ocean like edstevo recommends earlier in this thread (https://www.digitalocean.com/community/questions/socket-io-connection-timing-out)

  2. In the default Laravel Forge configuration on Digital Ocean, you do not need to manually start redis (using redis-server) like you do on your local computer. Redis is already started automatically and it appears to restart automatically just fine too.

  3. Just like mstnorris recommended, I used supervisor to ensure that my node server was automatically started and would stay that way.
    This Digital Ocean tutorial helped me set all that up: https://www.digitalocean.com/community/tutorials/how-to-install-and-manage-supervisor-on-ubuntu-and-debian-vps
    -- I would note with the above tutorial that you'll probably have to use sudo before a number of the commands, such as 'sudo supervisorctl reread' or else it'll throw up an error. Also, for those interested, below is what I used in my supervisor *.conf file:

[program:long_script]
command=/usr/bin/node /home/forge/default/path/to/index.js
autostart=true
autorestart=true
stderr_logfile=/var/log/long.err.log
stdout_logfile=/var/log/long.out.log

I'd also like to add that socket.io works very nicely with Angular. This was the tutorial that I used to get everything set up earlier (before this awesome one came along): http://kodeinfo.com/post/realtime-app-using-laravel-nodejs-angularjs-redis

One cool thing is that by using 'track by' in an Angular ng-repeat together with ngAnimate, you can animate only the new results in your collection. So, say I'm bringing in data from an API location, and only one part of that data has changed, Angular will animate just that one part, not the entire thing. I've done this in a new site that I did and it's very cool. I'll probably write up a tutorial for that at some point.

5 likes
lihaibh's avatar

@omurphy I already figured it out, in appears that redis doesnt have official release installation for windows and you actually need to build their library using Visual Studio or any C compiler in windows, compiling their entire library and run redis-sever.

Now i confirm it works.

mstnorris's avatar

@krballard94 I hadn't noticed the missing JS link. I had used blade syntax with the asset helper so that got escaped. Now fixed.

lihaibh's avatar

Anyone knows how can i "hide" the real ip of the server when using socket.io library

milon's avatar

awesome, perfectly worked for me.

just 1 question, redis-server is already running on my system on port 6379. but I have to start the redis-server again on another port. I have tried with multiple port, it works with port 3001-3010. but default redis-server doesn't work.

1 like
webofink's avatar

Fantastic post here! This is exactly what I'm looking for, and managed to get it working on a ubuntu 14.04 VM without any issues.

I was wondering, how would I go about using this with variables in the routes? For example, if I have /chatroom/1 and /chatroom/2, etc.

/chatroom/{id}

How would I pass the chatroom ID variable to the controller and event handler in such a way that I can make sure events fired for /chatroom/14 only get sent to a queue for /chatroom/14, and not to all connected sockets?

Thanks again for this!

1 like
omurphy's avatar

@webofink

There's probably a much better way to accomplish this, but here's what I did:

I made sure the page id was in the data that I was emitting to the socket and then used it to create a dynamic channel in my server side socket.js file like below:

redis.on("message", function(channel, message) {
        
        messageObj = JSON.parse(message);
        id = messageObj[0].id;

        channel = channel + "-" + id;

        client.emit(channel, message);
});

On the client side I had the socket listen to a dynamic channel that corresponds to its page. My solution here was pretty hacky. I created a route at /chatroom/{id}/id which just worked as an API that broadcasted the page's ID as JSON. Then I fetched this id (in my case using angular) and appended it to the 'channel' variable so the channel variable on the client matched the one of the server.

Here's the code I used in my Angular controller:

// fetch current URL code
var $url = $location.absUrl();
$url = $url.replace(/#.*$/,'');
var lastChar = $url.substr(-1);
    if (lastChar != '/') {         
       $url = $url + '/';            
}

// fetch id from /chatroom/{id}/id 
$http.get( $url + 'id').success(function(data) {
        var update = "comment.update-" + data.id;

        socket.on(update, function (data) {
        // do stuff
        // in this case update my scope with the latest data from socket.io
        $scope.data = data;   
        });
});

Although I'm using Angular the above process could be accomplished fairly similarly using vanilla JS or Jquery.

1 like
webofink's avatar

@omurphy that's fantastic! Thanks for this, I'm going to test it out and see how it goes. I'll report back. Cheers!

afrayedknot's avatar

If anyone is wondering how to combine websockets with Vue.js (I couldnt find a guide/tutorial anywhere) - I found a gist that has helped me: https://gist.github.com/koba04/28cfb6e8ef05fc5f844f

Basically you combine the gist code with the code that the OP gave - and you can easily combine websockets with Vue.js.

The trick was this bit:

socket.on('test-channel:App\\Events\\EventName', function(msg){
  vm.messages.push(msg);
});

You simply use the socket.on() function - and use vm.messages.push() to push a message from socket to vue.js.

After that - you just handle the VueJS push method as normal...

2 likes

Please or to participate in this conversation.