4

I am trying to create a real-time notification system using Laravel as the backend API Client and a SPA for my frontend, I am using React as the frontend, but for the below example I will be using a simple Vue.Js and blade that I have created in order to get a working example.

So, all in all, I have a route that fires an event, this is shown like the below example:

Route::get('fire', function () {
    // this fires the event
    $user = App\Models\User::find(1);
    event(new App\Events\CampaignUploadedWithSuccess($user, 'testing a notification'));
    return "event fired";
});

The event that it fires will look like this

<?php

namespace App\Events;

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

class CampaignUploadedWithSuccess implements ShouldBroadcast
{
    protected $user;

    public $notification;

    use Dispatchable, InteractsWithSockets, SerializesModels;

    /**
     * Create a new event instance.
     *
     * @param $user
     * @param $notification
     */
    public function __construct($user, $notification)
    {
        $this->user = $user;
        $this->notification = $notification;
    }

    /**
     * Get the channels the event should broadcast on.
     *
     * @return Channel|array
     */
    public function broadcastOn()
    {
        return ['notifications-channel.' . $this->user->id];
    }
}

So I am broadcasting on a channel called notification-channel.{userId}

I then have a socket.js file that I run using node.

That looks like this

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('notifications-channel.1', function(err, count) {
});
redis.on('message', function(channel, message) {
    console.log('Notification Recieved: ' + message);
    message = JSON.parse(message);
    io.emit(channel + ':' + message.event, message.data);
});
http.listen(3000, function(){
    console.log('Listening on Port 3000');
});

using node socket.js runs the server and firing the event does what I want like below:

Notification Recieved on localhost:3000

Happy Days! I am broadcasting to a channel..

However, I now have a blade file called test.blade that will pull in Vue and Socket.io

That looks like this

<!DOCTYPE html>
<html>
<head>
    <title>Laravel</title>
</head>
<body>
<h1>Notifications</h1>

<ul>
    <li v-repeat="notification: notifications">@{{ notifications }}</li>
</ul>

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/0.12.16/vue.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/1.3.7/socket.io.min.js"></script>

<script>
    var socket = io('http://localhost:3000');
    new Vue({
        el: 'body',
        data: {
            notifications: []
        },
        ready: function() {
            socket.on('notifications-channel.1', function(data) {
                alert(data.notification);
            });
        }
    });
</script>
</body>
</html>

And the goal here is to have the message alert when the notification-channel.1 has data broadcasted to it. Yet this does not work.

So my question is, how does one broadcast to a channel and consume that broadcast from the channel using socket.io, Laravel and Redis.

I am also a bit in the dark on Private Channels and how I can create a channel only for one user. The documentation is good, yet it does not provide real world examples of how to achieve this and have notifications consumed by multiple platforms.

2 Answers 2

2

I have also faced the same issue after searching i found the issue was due to the socket version. Please Make Sure the you have used same socket.io version in both the server end and client end you can verify this by first check the socket version in the package.json file like in my case you can see below

"devDependencies": {
    "socket.io": "^2.0.3",
    "socket.io-client": "^2.0.3",
 }

and then verify the socket version in you test.blade files like below

<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.0.3/socket.io.js"></script>

You can see both these are version are same.

and there is one problem in your blade files you emit socket event by key channel:event.name but on you blade files you can listen on only channel name without concatenating event name. please replace it with the below code

<script>
    var socket = io('http://localhost:3000');
    new Vue({
        el: 'body',
        data: {
            notifications: []
        },
        ready: function() {
            socket.on('notifications-channel.1:App\\Events\\CampaignUploadedWithSuccess', function(data) {
                alert(data.notification);
            });
        }
    });
</script>
Sign up to request clarification or add additional context in comments.

10 Comments

This makes a lot of sense, So I altered the blade file, and when I run it in the browser, I see my H1 tag, saying notifications, the console shows error connection refused, so I run node socket.js and that error goes away, in my mind this means that it connects to localhost:3000, I fire the event and it shows up in the server console but wont console log on the client... I made the versions match :)
I think I may be missing a connection to the socket in my socket.js file
The channel you broadcast your event has name notifications-channel then dynamically you pass the user id and on the client side you hard coded it with 1. does the user you are currently login has id 1 can u please confirm it.
no your socket.js file is ok i match it wih my it is ok no problem there
Ah then it must be in the blade file somewhere, I will debug from here...
|
0

I think you are missing the setup of the io look at

https://socket.io/docs/server-api/

Something like this would be needed on your nodejs server and/ or client.

io.on('connect', onConnect);

function onConnect(socket){
   socket.emit(channel + ':' + message.event, message.data);
}

Hope this helps

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.