Skip to content

Instantly share code, notes, and snippets.

@tonysm
Last active December 6, 2018 14:04
Show Gist options
  • Save tonysm/fd2f04cb48435c822c1dec660532117e to your computer and use it in GitHub Desktop.
Save tonysm/fd2f04cb48435c822c1dec660532117e to your computer and use it in GitHub Desktop.
Laravel Broadcasting Extended

Since we now have a way to deal with WebSockets in PHP/Laravel, I was wondering if we could extend the functionalities of Laravel Echo a bit more than the current API to actually use the bi-directional capabilities of WebSockets.

This gist is a draft of what it would be to use this extension.

Client Side Extension

Right now we can authenticate on private/presence channels. It's also possible to send client-side events in these channels using this API:

const channel = Echo.private(`chatroom.${this.chatroom.id}`);

channel.whisper({
    type: 'typing',
    user: this.user,
});

But there is no way to talk to the backend thru the socket channel. I think a good API would be something like this:

const channel = Echo.private(`chatrooms.${this.chatroom.id}`);

channel.push({ message: 'lorem ipsum' })
  .then((response) => {
      console.log(response); // whatever is returned from the backend (?)
  })
  .catch((error) => {
      switch(error.code) {
          case 422:
              console.log(error.data.errors); // validation errors
              break;
          default:
              console.log('Something went wrong.');
              console.log(error);
              break;
      }
  });

Not sure yet, looks good so far.

Backend Extension

We already got channel classes that looks something like this:

<?php

namespace App\Channels;

use App\User;
use App\ChatRoom;

class ChatRoomChannel
{
    public function join(User $user, ChatRoom $chatRoom)
    {
        if ($user->canJoin($chatRoom)) {
            return $user->toArray();
        }
    }
}

Cool. We can maybe take the Phoenix approach and implement a handle method for dealing with income messages:

<?php

namespace App\Channels;

use App\User;
use App\ChatRoom;
use Illuminate\Support\Str;
use Some\WebSockets\Library\Socket;
use App\Events\ChatRooms\NewMessage;
use Some\WebSockets\Library\IncomeMessageRequest;

class ChatRoomChannel
{
    public function join(User $user, ChatRoom $chatRoom, Socket $socket)
    {
        if ($user->canJoin($chatRoom)) {
            $socket->assign(['chatroom_id' => $chatRoom->id]);
            
            return $user->toArray();
        }
    }
    
    public function handle(User $user, IncomeMessageRequest $request, Socket $socket)
    {
        $chatRoom = ChatRoom::findOrFail(
            // you can rely on assignments done in the join method for this socket connection.
            $socket->assigns('chatroom_id')
        );

        $messageId = Str::uuid()->toString();
        
        broadcast(new NewMessage(
            $chatRoom,
            $messageId,
            $request->input('message')
        ))->toOthers();
        
        return [
            'message_id' => $messageId,
        ];
    }
}

This also might allow us to deal with stuff like the authematic validation of income messages (like Laravel deals with Form Request validation):

<?php

namespace App\WebSockets\Requests\ChatRooms;

use Some\WebSockets\Library\IncomeMessageRequest;

class NewMessageRequest extends IncomeMessageRequest
{
    public function rules()
    {
        return [
            'message' => ['required', 'min:3'];
        ];
    }
}

Then we can type-hint it in the channel class:

<?php

namespace App\Channels;

use App\User;
use App\ChatRoom;
use Illuminate\Support\Str;
use Some\WebSockets\Library\Socket;
use App\Events\ChatRooms\NewMessage;
use App\WebSockets\Requests\ChatRooms\NewMessageRequest;

class ChatRoomChannel
{
    public function join(User $user, ChatRoom $chatRoom, Socket $socket)
    {
        if ($user->canJoin($chatRoom)) {
            $socket->assign(['chatroom_id' => $chatRoom->id]);
            
            return $user->toArray();
        }
    }
    
    public function handle(User $user, NewMessageRequest $request, Socket $socket)
    {
        $chatRoom = ChatRoom::findOrFail(
            $socket->assigns('chatroom_id')
        );
        
        $messageId = Str::uuid()->toString();
        
        broadcast(new NewMessage(
            $chatRoom,
            $messageId,
            $request->input('message')
        ))->toOthers();
        
        return [
            'message_id' => $messageId,
        ];
    }
}

Just like Laravel's Form class, validation would happen before the request hits the method and a response would be sent back to the user.


As I said, this is just a draft. Not sure if we can implement such a thing, nor about the scaling of this, as the WebSockets server would deal with much more than WebSockets connections.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment