Search code examples
laravellaravel-echo

Same user joining multiple times to presence channel (laravel echo)


I am using the Laravel Echo presence channel to show how many people are online on a certain page in which they start their exam session. However if I a user is already joined to the channel another session of the user using that page will not be taken into account. Laravel Echo will ignore any other joins of the same user. Which makes sense if we want to build some kind of chat room. However, in my case, I want to know how many active exams are being taken by users even if the users are the same.

Is there any way to modify this default behavior?

in order to know which exam a user is started I modify authEndpoint to include its id.

window.Echo = new Echo({
    broadcaster: 'pusher',
    key: 'sapio',
    wsHost: window.location.hostname,
    wsPort: 80,
    wssPort: 443,
    disableStats: true, // we dont want pusher send statistic
    forceTLS: false,
    enabledTransports: ['ws', 'wss'],
    authEndpoint: window.location.hostname + "/broadcasting/auth?examId=" + examId
});

presence channel:

Broadcast::channel('live.exams', function ($user) {
    //Here I take the exams id that is I provided in the authorization endpoint
    return request('examId');
}, ['guards' => ['web','api']]);

Solution

  • I found a way around it.

    In order to uniquely identify every broadcast channel request, Laravel use a broadcast identifier. take a look into validAuthenticationResponse in vendor/laravel/framework/src/Illuminate/Broadcasting/Broadcasters/PusherBroadcaster.php

       public function validAuthenticationResponse($request, $result)
        {
            if (Str::startsWith($request->channel_name, 'private')) {
                return $this->decodePusherResponse(
                    $request, $this->pusher->socket_auth($request->channel_name, $request->socket_id)
                );
            }
    
            $channelName = $this->normalizeChannelName($request->channel_name);
    
            $user = $this->retrieveUser($request, $channelName);
    
            $broadcastIdentifier = method_exists($user, 'getAuthIdentifierForBroadcasting')
                            ? $user->getAuthIdentifierForBroadcasting()
                            : $user->getAuthIdentifier();
    
            return $this->decodePusherResponse(
                $request,
                $this->pusher->presence_auth(
                    $request->channel_name, $request->socket_id,
                    $broadcastIdentifier, $result
                )
            );
        }
    

    The $broadcastIdentifier can be customized by creating getAuthIdentifierForBroadcasting method for the user. By default the identifier will be the user id. However you can define a custom id that will not be repeated. Then, the same user will be shown in the presence channel.

    Therefore in my case I add followings to my User and set the custom id whenever I need it.

        private $CustomAuthIdentifierForBroadcasting = null;
    
        public function setCustomAuthIdentifierForBroadcasting($id) {
            $this->CustomAuthIdentifierForBroadcasting = $id;
        }
    
        public function getAuthIdentifierForBroadcasting() {
            return $this->CustomAuthIdentifierForBroadcasting ?? $this->getAuthIdentifier();
        }