I am working on Laravel 9 where I install Laravel WebSocket, Laravel Echo, and Pusher PHP server.
By the way, I didn't use the official Pusher application, just using the package as per Laravel-WebSocket package documentation suggested.
User case - I want to update the site model value and send a notification (broadcast and mail) to the end user as soon as the user deletes a site.
Everything is installed and working fine but I found some glitches in the Laravel-WebSocket, Pusher package.
I have created the following event which will broadcast to the end user.
SiteDelete.php
<?php
namespace App\Events;
use App\Models\Site;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class SiteDeleted implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
/**
* The site instance.
*
* @var \App\Models\Site
*/
public $site;
/**
* The name of the queue connection to use when broadcasting the event.
*
* @var string
*/
public $connection = 'database';
/**
* The name of the queue on which to place the broadcasting job.
*
* @var string
*/
public $queue = 'default';
/**
* Create a new event instance.
*
* @return void
*/
public function __construct(Site $site)
{
$this->site = $site;
}
/**
* Get the channels the event should broadcast on.
*
* @return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
// return new PrivateChannel('site.delete.'.$this->site->id); // this is not working.
// return [new PrivateChannel('site.delete.'.$this->site->id)]; // this is not working.
return [new PrivateChannel('site.delete.'.$this->site->id), new Channel('mock')]; // This will call but I need to pass two channels intentionally.
}
/**
* Get the data to broadcast.
*
* @return array
*/
public function broadcastWith()
{
return ['id' => $this->site->id];
}
}
app.js
Echo.private("site.delete.1")
.listen('SiteDeleted', (e) => {
console.log("SiteDeleted");
console.log(JSON.stringify(e));
})
Echo.private('App.Models.User.7')
.notification((notification) => {
console.log("App.Models.User");
console.log(notification);
});
Problem
- As you can see comments in my event class's
broadcastOn
method where I need to pass two channels. One is real and the second one is fake. So ultimately you need to pass at least two channels so the pusher request will have a channels
parameter [which will work] but the channel
parameter never works[i.e when you pass a single channel].
- I can able to send custom events from the WebSocket GUI. i.e from http://localhost:8000/laravel-websockets URL. but those events are never caught by the front end unless I do it the dirty way.
- The notifications are never caught by the front end due to this
channel
and channels
parameter issue.
Dirty Way[Yes I know we should never touch the vendor folder but just curious to know why the things are not working]
- I checked the vendor folder very deeply and I come to know, in the
vendor/pusher/pusher-php-server/src/Pusher.php
under the make_event
function if I update the following line then it starts working without passing two channels.
vendor/pusher/pusher-php-server/src/Pusher.php
private function make_event(array $channels, string $event, $data, array $params = [], ?string $info = null, bool $already_encoded = false): array
{
// if (count($channel_values) == 1) {
// $post_params['channel'] = $channel_values[0];
// } else {
// $post_params['channels'] = $channel_values;
// }
$post_params['channels'] = $channel_values;
}
My Research
- As the WebSocket package suggests installing pusher-php-server version 3.0 but I install the latest one i.e 7. Version 3.0 is incompatible with Laravel 9. But I can't and don't want to install the older version.
- I think the WebSocket package is not able to send the event and data on a single channel with a newer version of pusher-php-server.
- I can't raise an issue (or blame it) for Pusher SDK because we are just replacing the package and I think the Pusher SDK package is working fine when you use their credentials(ie. you have to create an app on Pusher).
- Even if you can check on the WebSocket dashboard i.e
http://localhost:8000/laravel-websockets
when you send the event it will never catch in the front end. But as soon as you update the Pusher.php file it starts catching an event on the front end.
- due to the above reason, as you know the notification are sent to the user on their private channels, So I can't add a mock channel for notification as I did for my event, so notification will never catch by the frontend application.
composer.json
"beyondcode/laravel-websockets": "^1.13",
"pusher/pusher-php-server": "^7.2",
"laravel/framework": "^9.19",
package.json
"pusher-js": "^7.5.0",
"laravel-echo": "^1.14.2",
I tried the explicit way as well i.e using the pusher SDK's functions[which are giving 200 status code] but not working. As soon as I do it the dirty way it starts working, I mean everything starts working without any issue.
public function pusherTesting(Request $request)
{
$path = "/apps/123456/events";
$settings = [
'scheme' => 'http',
'port' => '6001',
'path' => '',
'timeout' => '30',
'auth_key' => '1b5d6e5b1ab73b',
'secret' => '3739db6a99c1ba',
'app_id' => '123456',
'base_path' => '/apps/123456',
'host' => '127.0.0.1',
];
$params = [];
$body = '{"name":"Illuminate\\Notifications\\Events\\BroadcastNotificationCreated","data":"{\"site_id\":1,\"domain_url\":\"yucentipede-tuvo.blr3.instawp-testing.xyz\",\"save\":\"socket\",\"id\":\"2f53aac0-8d83-45f4-962d-516c1c8bc97c\",\"type\":\"App\\\\Notifications\\\\SiteDeletedNotification\"}","channels":["private-App.Models.User.7"]}';
$params['body_md5'] = md5($body);
$params_with_signature = \Pusher\Pusher::build_auth_query_params(
$settings['auth_key'],
$settings['secret'],
'POST',
$path,
$params
);
$headers = [
'Content-Type' => 'application/json',
'X-Pusher-Library' => 'pusher-http-php 7.2.1'
];
$client = new \GuzzleHttp\Client();
try {
$response = $client->post(ltrim($path, '/'), [
'query' => $params_with_signature,
'body' => $body,
'http_errors' => false,
'headers' => $headers,
'base_uri' => 'http://127.0.0.1:6001'
]);
} catch (Exception $e) {
print_r($e->getMessage());
}
$response_body = json_decode($response->getBody(), false, 512, JSON_THROW_ON_ERROR);
echo $status = $response->getStatusCode();
die;
}