Laravel-websockets: Exist a way to implement webhooks?

Created on 9 Jan 2019  路  18Comments  路  Source: beyondcode/laravel-websockets

The only missing feature for me fully migrate from pusher is the event webhooks, I could not find anything about it. Is something planned to have? I can contribute to that, just want to know if you guys have something in mind or some direction to follow.

Regards,

Most helpful comment

@stefandanaita sorry for the delay... I just have registered a singleton in my AppServiceProvider:

    public function register()
    {
        $this->app->singleton('websockets.router', function () {
            return new Router();
        });
    }

Router.php

<?php

namespace App\WebSockets\Server;

use App\WebSockets\WebSockets\WebSocketHandler;
use BeyondCode\LaravelWebSockets\HttpApi\Controllers\FetchChannelController;
use BeyondCode\LaravelWebSockets\HttpApi\Controllers\FetchChannelsController;
use BeyondCode\LaravelWebSockets\HttpApi\Controllers\FetchUsersController;
use BeyondCode\LaravelWebSockets\HttpApi\Controllers\TriggerEventController;

class Router extends \BeyondCode\LaravelWebSockets\Server\Router
{

    public function echo()
    {
        $this->get('/app/{appKey}', WebSocketHandler::class);

        $this->post('/apps/{appId}/events', TriggerEventController::class);
        $this->get('/apps/{appId}/channels', FetchChannelsController::class);
        $this->get('/apps/{appId}/channels/{channelName}', FetchChannelController::class);
        $this->get('/apps/{appId}/channels/{channelName}/users', FetchUsersController::class);
    }

}

WebSocketHandler.php

<?php

namespace App\WebSockets\WebSockets;

use Ratchet\ConnectionInterface;
use Ratchet\RFC6455\Messaging\MessageInterface;

class WebSocketHandler extends \BeyondCode\LaravelWebSockets\WebSockets\WebSocketHandler
{

    public function onMessage(ConnectionInterface $connection, MessageInterface $message)
    {
        // My custom code...

        parent::onMessage($connection, $message);
    }

}

but sure, this is a weird way. Extending as you said in #21 I think that is a better way!

All 18 comments

I also want this!
For now (maybe it is a wrong way, but it works) I just have created a class that extends BeyondCode\LaravelWebSockets\WebSockets\WebSocketHandler, and a custom Router, and put my own logic in certain hooks...

This would be really cool but possibly also tricky looking at the docs it has a few "specialties" that requires some work to make this work correctly.

For example: https://pusher.com/docs/webhooks#delay

Just spit-ballin' here, but it might be a good one to write events to an Redis buffer/list and let another process/cron handle the actual sending to prevent adding to much work to the websocket server process causing it to block more than needed (which also would be an easy-ish way to get batched hook working easily).

@stayallive So basically the main feature to be develop here could be just an option to save events somewhere (like redis), and each individual project cares about what to do with the stored events?

@joaokamun well, I would implement a php artisan websockets:webhooks command or something like that in this package so you could just add to a cron or daemon running that command which handles all the webhook sending.

I also want this!
For now (maybe it is a wrong way, but it works) I just have created a class that extends BeyondCode\LaravelWebSockets\WebSockets\WebSocketHandler, and a custom Router, and put my own logic in certain hooks...

@enzonotario could you please share an example of what you've done? Thanks!

Update: Managed to get it done by extending the websockets.router singleton in the IoC. Thanks for the idea, works nice and smooth. However, a nice webhooks implementation that allows 2 way communication between Laravel and the frontend would be amazing. CC @mpociot @freekmurze

@stefandanaita sorry for the delay... I just have registered a singleton in my AppServiceProvider:

    public function register()
    {
        $this->app->singleton('websockets.router', function () {
            return new Router();
        });
    }

Router.php

<?php

namespace App\WebSockets\Server;

use App\WebSockets\WebSockets\WebSocketHandler;
use BeyondCode\LaravelWebSockets\HttpApi\Controllers\FetchChannelController;
use BeyondCode\LaravelWebSockets\HttpApi\Controllers\FetchChannelsController;
use BeyondCode\LaravelWebSockets\HttpApi\Controllers\FetchUsersController;
use BeyondCode\LaravelWebSockets\HttpApi\Controllers\TriggerEventController;

class Router extends \BeyondCode\LaravelWebSockets\Server\Router
{

    public function echo()
    {
        $this->get('/app/{appKey}', WebSocketHandler::class);

        $this->post('/apps/{appId}/events', TriggerEventController::class);
        $this->get('/apps/{appId}/channels', FetchChannelsController::class);
        $this->get('/apps/{appId}/channels/{channelName}', FetchChannelController::class);
        $this->get('/apps/{appId}/channels/{channelName}/users', FetchUsersController::class);
    }

}

WebSocketHandler.php

<?php

namespace App\WebSockets\WebSockets;

use Ratchet\ConnectionInterface;
use Ratchet\RFC6455\Messaging\MessageInterface;

class WebSocketHandler extends \BeyondCode\LaravelWebSockets\WebSockets\WebSocketHandler
{

    public function onMessage(ConnectionInterface $connection, MessageInterface $message)
    {
        // My custom code...

        parent::onMessage($connection, $message);
    }

}

but sure, this is a weird way. Extending as you said in #21 I think that is a better way!

@stefandanaita You sir are BRILLIANT!!!!!!!!!! This was a lifesaver!!! Thank you so much. Quick question though, do you know i can know which client disconnect from the app when the connection is closed? That's the whole point of extending this package. I need to tell the chat app he is offline after a non graceful log out. Thanks.

@stefandanaita You sir are BRILLIANT!!!!!!!!!! This was a lifesaver!!! Thank you so much. Quick question though, do you know i can know which client disconnect from the app when the connection is closed? That's the whole point of extending this package. I need to tell the chat app he is offline after a non graceful log out. Thanks.

@nicolasvahidzein Did you figured out how to identify user once we extend the WebSocketHandler?

Hi,
In my custom onClose function how can I get user's id or username if I use presence channels?

@noobshooter27 The connection can make use of the channel manager and you can get the connection from there.

Hi @rennokki , may I know how can I get the private channel name from the $connection in my custom onClose function?
Tried this $this->channelManager->getChannels($connection->app->id), but it seem like don't have the channel name inside.

Hi @rennokki , may I know how can I get the private channel name from the $connection in my custom onClose function?
Tried this $this->channelManager->getChannels($connection->app->id), but it seem like don't have the channel name inside.

@hengjingyoong
try dd($this->channelManager);you'll find everything there

@hengjingyoong Have you called parent::onClose($connection, $exception) before anything else in your custom method?

@rennokki, I just used the original WebSocketHandler to test it out.

Thanks @noobshooter27 , finally I managed to find out what is the channel name from the closing connection.

What I did was defined a custom function in BeyondCode\LaravelWebSockets\WebSockets\Channels\Channel

    public function checkConnection($socketId)
    {
        if ($this->subscribedConnections[$socketId] ?? null) {
            return $this->channelName;
        }

        return false;
    }

and then in BeyondCode\LaravelWebSockets\WebSockets\WebSocketHandler, get the channel name before it closed.

    public function onClose(ConnectionInterface $connection)
    {
        $allChannels = $this->channelManager->getChannels($connection->app->id);

        foreach($allChannels as $channel){
            if ($channelName = $channel->checkConnection($connection->socketId)) {
                break;
            }
        }
        dd($channelName);

        $this->channelManager->removeFromAllChannels($connection);

        DashboardLogger::disconnection($connection);

        StatisticsLogger::disconnection($connection);
    }

But I'm not sure if that is secure to work in this way.

ps: my working scenario is attach the user_id to the private channel name, that's why I need to get back the user_id from channel name, and notify my backend which user is offline. This is to handle the non graceful log out mentioned by @nicolasvahidzein

@hengjingyoong It's highly recommended to not change any vendor/* files.

@hengjingyoong It's highly recommended to not change any vendor/* files.

Do you have any though on getting the channel name without adding my own function in BeyondCode\LaravelWebSockets\WebSockets\Channels\Channel?

Finally I figure out how to get the closing channel name.

Here is it.

<?php

namespace App\WebSocket;

use Ratchet\ConnectionInterface;
use BeyondCode\LaravelWebSockets\WebSockets\WebSocketHandler as BaseWebSocketHandler;

class CustomWebSocketHandler extends BaseWebSocketHandler
{
    public function onClose(ConnectionInterface $connection)
    {
        $allChannels = $this->channelManager->getChannels($connection->app->id);

        foreach($allChannels as $channelName => $channel){

            if($channel->getSubscribedConnections()[$connection->socketId] ?? null) {
                $closingChannel = $channel->getChannelName();
                break;
            }
        }

        dd($closingChannel);

        parent::onClose($connection);
    }
}

ps: I was missed out the getSubscribedConnections function in BeyondCode\LaravelWebSockets\WebSockets\Channels\Channel lol

Was this page helpful?
0 / 5 - 0 ratings

Related issues

sudden-break picture sudden-break  路  4Comments

henzeb picture henzeb  路  4Comments

darklight9811 picture darklight9811  路  4Comments

ElegantSoft picture ElegantSoft  路  4Comments

myckhel picture myckhel  路  3Comments