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.
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.
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.