Abraham A. Abraham A. - 1 month ago 25
PHP Question

Laravel Echo won't catch events with socket.io

I using Redis with Socket.io to setup real time updates but Laravel Echo isn't catching events.

If I use an instance of io() it works just fine. Here is my code in my vue app to listen for events:

Will NOT catch events

window.Echo.channel('users')
.listen('UserCreated', event => {
console.log(event);
this.all_users.push(event.user);
this.filter_data();
});


Will catch events

window.socket = io(window.location.origin+':3000');
window.socket.on('users:App\\Events\\UserCreated', function(data){
console.log(data);
console.log('from socket');
this.all_users.push(data.user);
this.filter_data();
}.bind(this));


My node js code to push events.

var server = require('http').Server();

var io = require('socket.io')(server);

var Redis = require('ioredis');

var redis = new Redis();

redis.subscribe('users', function(error, count){
console.log(error);
console.log(count);
});

redis.on('message', function(channel, message){
console.log(channel);
message = JSON.parse(message);
console.log(message);
const emmitChannel = `${channel}:${message.event}`;
console.log(emmitChannel);
io.emit(emmitChannel, message.data);
});

server.listen(3000);


My Laravel Event Broadcast

<?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;


//Models
use App\User;

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

public $user;

/**
* Create a new event instance.
*
* @return void
*/
public function __construct(User $user)
{
$this->user = $user;

$this->dontBroadcastToCurrentUser();
}

/**
* Get the channels the event should broadcast on.
*
* @return Channel|array
*/
public function broadcastOn()
{
return ['users'];
}
}


I've tried changing the way events are passed to Laravel Echo since by default it receives them as
users:App\Events\UserCreated
but I haven't had luck.

Answer Source

I finally managed to figure this out. I looked through the JS files and managed to pin it down to the fact that the code in this line has the try catch section not actually throw errors if the Laravel channels.php check to who can listen to a channel doesn't return a user model instance as it should.

Something like this in Channels.php will throw out that error:

Broadcast::channel('users', function ($user) {
    if($user->type == 'admin'){
         return $user;
    }else{
         return false;
    }
});

If the $user doesn't pass the if check Laravel will return false. Since it's not returning JSON and simply false I think it should log that the response body did not pass the JSON.parse() function so you can see that in the terminal. Something like this:

try {
     body = JSON.parse(response.body);
} catch (e) {
     Log.warning('Response body provided is not expected JSON. Response body provided ${response.body}');
     body = response.body;
}

This way we can see if Laravel isn't returning a user model instance.

So when you don't get a user model instance as expected, there is no user object for the code to attach the sockedId to because it's undefined. There was never any JSON to make the object with.

I would think the code should stop trying to connect to the presence or private channel if you don't get a user instance either rather than trying to attach a socket ID.

Anyways, to actually return a user make sure you are logged in using Laravel's Auth system. If you didn't use that you might have to do some work around to get it to work. If you are not using it then this function in Laravel will throw a 403 error because it can't find you to be logged in.

I had to over write the function that checks the password hash algorithm to a custom algorithm with a provider so I don't mess with the Laravel core and make the user model extend the class Laravel is looking for.

Then in your channels.php file just make sure what check you're doing you're actually returning the $user in the callback function from the auth check the Broadcaster.php file does. Something like this:

Broadcast::channel('users', function ($user) {
     return $user;
});

That's a super simple private/presence channel but with a logged in user and returning the $user this will not throw out this error.

Also, make sure you're calling socket.io in your Echo instance like this:

window.Echo = new Echo({
    broadcaster:  'socket.io',
    host: window.location.origin + ':6001'
});