I've worked on getting database and broadcast notifications to play nice for quite a while, and I've finally got it working and identified an issue I don't know how to solve.
The root cause seems to be that the notification from pusher is getting back to the app prior to when the database notification is available to select.
This results in my notification icon showing up on the front end, but the notifications array is empty:
public function getListeners()
{
return [
"echo-private:users.{$this->user->id},StatementCompleted" => 'notifyUser',
];
}
public function notifyUser(mixed $notification)
{
if (!empty($notification)) {
$this->showNotificationsBadge = true;
ray('notification!');
$this->refreshNotifications();
ray($this->notifications); <-- THIS IS EMPTY
}
}
public function refreshNotifications()
{
$this->notifications = $this->user->unreadNotifications()->get();
$this->notificationCount = $this->user->unreadNotifications()->count();
}
HOWEVER, if I add sleep(5)
into the notifyUser()
method like so:
public function notifyUser(mixed $notification)
{
if (!empty($notification)) {
$this->showNotificationsBadge = true;
ray('notification!');
sleep(5); <-- ADDED THIS
$this->refreshNotifications();
}
}
it results in the notifications array containing the notification and everything works fine. So my theory is that it's a timing issue.
How do I ensure my database notification exists before the pusher notification triggers my livewire front-end refresh?
EVENT/LISTENER/NOTIFICATION CLASSES:
Here's my event:
class StatementCompleted implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $statement;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct(Statement $statement)
{
$this->statement = $statement;
}
/**
* Get the channels the event should broadcast on.
*
* @return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new PrivateChannel('users.' . $this->statement->uploadedBy->id);
}
}
and here's the event listener class:
class HandleStatementCompletedEvent implements ShouldQueue
{
/**
* Create the event listener.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* @param object $event
* @return void
*/
public function handle($event)
{
Notification::send($event->statement->uploadedBy, new SendStatementCompletedNotification($event->statement));
}
}
and here's the notification class:
class SendStatementCompletedNotification extends Notification implements ShouldQueue, ShouldBroadcast
{
use Queueable;
public $statement;
/**
* Create a new notification instance.
*
* @return void
*/
public function __construct(Statement $statement)
{
$this->statement = $statement;
}
/**
* Get the notification's delivery channels.
*
* @param mixed $notifiable
* @return array
*/
public function via($notifiable)
{
return ['database', 'broadcast'];
}
/**
* Get the broadcastable representation of the notification.
*
* @param mixed $notifiable
* @return BroadcastMessage
*/
public function toBroadcast($notifiable)
{
return new BroadcastMessage([
'title' => 'Statement Processed',
'message' => "Your statement {$this->statement->original_file_name} has been processed.",
'user_id' => $this->statement->uploadedBy->id
]);
}
/**
* Get the array representation of the notification.
*
* @param mixed $notifiable
* @return array
*/
public function toDatabase($notifiable)
{
return [
'title' => 'Statement Processed',
'message' => "Your statement {$this->statement->original_file_name} has been processed.",
'user_id' => $this->statement->uploadedBy->id
];
}
}