Using livewire with echo and pusher to send a friend request notification

Hello,I’m really confused about laravel and notifcations in combination with livewire and echo / pusher. I’m using this package to manage friendships between users (among other things)
https://github.com/multicaret/laravel-acquaintances.

I have a livewire component where users can search for other users to send them a friend request,

 <button wire:click="sendFriendRequest({{$user->id}})" class="w-24 text-right cursor-pointer flex justify-end">
            <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
              <path fill-rule="evenodd"
                    d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-11a1 1 0 10-2 0v2H7a1 1 0 100 2h2v2a1 1 0 102 0v-2h2a1 1 0 100-2h-2V7z"
                    clip-rule="evenodd"/>
            </svg>
          </button>

The function sendFriendRequest is called on the button click to send a request. The befriend method is part of the package, I would like to notify said user who was send a friend request, via a notification.

  public function sendFriendRequest(int $id)
  {
    Auth::user()->befriend(User::find($id)->first());

    Auth::user()->notify(new SendFriendRequest());
  }

The notifiable class

<?php

namespace App\Notifications;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\BroadcastMessage;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;

class SendFriendRequest extends Notification
{
  use Queueable;

  public $friendRequest;

  /**
   * Create a new notification instance.
   *
   * @return void
   */
  public function __construct()
  {
  }

  /**
   * Get the notification's delivery channels.
   *
   * @param mixed $notifiable
   * @return array
   */
  public function via($notifiable)
  {
    return ['database', 'broadcast'];
  }

  /**
   * Get the mail representation of the notification.
   *
   * @param mixed $notifiable
   * @return \Illuminate\Notifications\Messages\MailMessage
   */
  public function toMail($notifiable)
  {
    return (new MailMessage)
      ->line('The introduction to the notification.')
      ->action('Notification Action', url('/'))
      ->line('Thank you for using our application!');
  }

  /**
   * Get the array representation of the notification.
   *
   * @param mixed $notifiable
   * @return array
   */
  public function toArray($notifiable)
  {
    return [
      //
    ];
  }

  public function toDataBase($notifiable)
  {
    return [
      'name' => $notifiable->name,
      'message' => 'Has send you a friend request',
    ];
  }
}

And here is where my confussion starts. I read the laravel docs about notifications, and the livewire docs about using laravel echo in combination with livewire. but it seems livewire will only work with events? and not notifcations, so is it somehow possible to use livewire to listen to a notification event ? in the $listeners property ? and then send a notifcation to the user? I’m sorry if this is a bit confusing as this is my first time working with these sort of things, but the offical laravel and livewire docs are all very unclear about this sort of thing, and the laravel docs contain so much information that I’m honestly a bit overwhelmed about how to handle this.

Laravel echo config

import Echo from 'laravel-echo';

window.Pusher = require('pusher-js');

window.Echo = new Echo({
    broadcaster: 'pusher',
    key: process.env.MIX_PUSHER_APP_KEY,
    cluster: process.env.MIX_PUSHER_APP_CLUSTER,
    forceTLS: true
});

Hi,
You can combine notifications and events to achieve what you want
First the notification for sending an email and store that notification in the database to fetch it when the event is fired.

You can use a broadcast in notification but for some reason with livewire,I was getting some authorization problems that don’t happen if you use it without livewire.

1- In the constructor you are going to accept a parameter if need it to send that data in the notification, declare your public property and assign the value in the constructor. Let,s said you send the user that is sending the notification as a parameter

2- In your notification class in the via the method you will have something like
public function via($notifiable)
{
return [‘mail’, ‘database’];
}

Then you going to use the method

public function toMail($notifiable)
{
return (new MailMessage)
->subject(‘Friend Request’)
->line(“Hi,’ $notifcable->name, “)
->line(”$this->user->name sent you a friend request”)
->action(‘Notification Action’, url(’/’)) ///Here you can add the URL where the user can accept that request
->line(‘Thank you for using our application!’);
}

public function toDataBase($notifiable)
{
return [
‘name’ => $notifiable->name,
‘message’ => ‘Has send you a friend request’,
];
}

3- Let’s create a event and called SendFriendRequest
Then in that class you are going to implements ShouldBroadcastNow

Then in the broadcastOn you are going to specify in which channel you want to send that notification
we want that notification only for the user that was sent to, so we use private channel that with need to authorize later
In the constructor accept the user o user id that you are going to use to identify that channel
public $user
public function __construct(User $user)
{
$this->user=$user;
}

  public function broadcastOn()
    {
        return new PrivateChannel('SendFriendRequest.'.$this->user->id);
       /// this way we ensure that only that user is receiving the notification
    }

Now let’s authorize that channel, go to route folder then select channels.php, there you will create your broadcasting channel authorization

Broadcast::channel('SendFriendRequest.{id}', function ($user,$id) {
        return $user->id === (int)$id;
});

//Now every time someone is connected to this specific broadcast channel this rule will check if the user can listen for that channel.

Now in your livewire component, you are going to listen for that event, then when that event fire, you can call any method that you want, in this case can be a notification badge or sweet alert anything you want.

Now let’s define the listener in you livewire component

 public function getListeners()
    {  $Id=Auth::id();
        return [
         "echo-private:SendFriendRequest.{$Id},SendFriendRequest" => 
        'showfriendrequest'];
    }

/// here you have first, the channel you are listening to, second the class of the event, and third the method to call when that event is fire

in livewire , where you send friend request after notification, fire the event like this

$currentuser=Auth::user(); // Current user that is sending the request

event(new SendFriendRequest($currentuser));

then the method to be called when that event fire

public function showfriendrequest($data)
{
      dd($data) // if in the event you send any data you get it like this, you can use whatever variable name
}

Note due to the high cost of pusher, I use Laravel WebSockets https://beyondco.de/docs/laravel-websockets/getting-started/introduction,,, but before moving there try with pusher that is easier to start

To understand better notification, check this in laracast

I hope you are clear now, any question let me know

1 Like

Hey, thank you for helping. I followed your steps and, for the most part, it works fine but in my livewire component I cannot receive the event inside of my livewire method when the event is fired.

I have the sendfriendrequest event

<?php

namespace App\Events;

use App\Models\User;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class SendFriendRequest implements ShouldBroadcastNow
{
  use Dispatchable, InteractsWithSockets, SerializesModels;

  public $user;

  /**
   * Create a new event instance.
   *
   * @return void
   */
  public function __construct(User $user)
  {
    $this->user = $user;
  }

  /**
   * Get the channels the event should broadcast on.
   *
   * @return \Illuminate\Broadcasting\Channel|array
   */
  public function broadcastOn()
  {
    return new PrivateChannel('SendFriendRequest.'.$this->user->use->id);
  }
}

Then I have the notification

<?php

namespace App\Notifications;

use App\Models\User;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;
use Illuminate\Notifications\Messages\BroadcastMessage;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;

class SendFriendRequest extends Notification
{
  use Queueable;

  public $user;

  /**
   * Create a new notification instance.
   *
   * @return void
   */
  public function __construct($user)
  {
    $this->user = $user;
  }

  /**
   * Get the notification's delivery channels.
   *
   * @param mixed $notifiable
   * @return array
   */
  public function via($notifiable)
  {
    return ['database', 'mail'];
  }

  /**
   * Get the mail representation of the notification.
   *
   * @param mixed $notifiable
   * @return \Illuminate\Notifications\Messages\MailMessage
   */
  public function toMail($notifiable)
  {
    return (new MailMessage)
      ->subject('Friend request')
      ->line("Hi, {$notifiable->name}")
      ->line("{$this->user->name} has sent you a friend request")
      ->action('Notification Action', url('/'))
      ->line('Thank you for using our application!');
  }

  /**
   * Get the array representation of the notification.
   *
   * @param mixed $notifiable
   * @return array
   */
  public function toDataBase($notifiable)
  {
    return [
      'name' => $notifiable->name,
      'message' => 'Has send you a friend request',
    ];
  }
}

The channel

Broadcast::channel('SendFriendRequest.{id}', function ($user,$id) {
  return $user->id === (int)$id;
});

And in my livewire component I have the listeners

  public function getListeners()
  {
    $user_id = Auth::id();

    return ["echo-private:SendFriendRequest.{$user_id},SendFriendRequest" => 'showfriendrequest'];
  }

and the listeners callback


  public function showfriendrequest($data)
  {
    dd($data); 
  }

When I send someone a friend request, the mail is being sent and the notification is saved inside of my database but my livewire component will not receive the event.

Hi, i forgot to write the way you fire the event

in livewire , where you send friend request after notification, fire the event like this
$currentuser=Auth::user(); // Current user that is sending the request
event(new SendFriendRequest($currentuser));

there is a mistake in the private channel
return new PrivateChannel(‘SendFriendRequest.’.$this->user->use->id);//added use by mistake;

must be
return new PrivateChannel(‘SendFriendRequest.’.$this->user->id);
After this check in Pusher if you are receiving any data.

also enable queue worker, cause laravel enable queue by default in notifications
run php artisan queue:work

Hey, it’s working now, but the notifications are not real time e.g. I have two browser sessions open (one on chrome and one on firefox) if I send a notification from user1 who is on chrome to user2 who is on firefox, I have to reload the page in order to see the notification appear, I get all of the notifications using this component

<?php

namespace App\Http\Livewire;

use App\Models\User;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Auth;
use Livewire\Component;

class Notifications extends Component
{
  public $notifications;

  public function render()
  {
    $this->notifications = Auth::user()->unreadNotifications()->get();
    return view('livewire.notifications')->with(['notifications' => $this->notifications]);
  }

  public function markAsRead(string $id)
  {
    Auth::user()->unreadNotifications()->where('id', $id)->first()->markAsRead();
  }

  public function acceptFriendRequest(int $id)
  {
    Auth::user()->acceptFriendRequest(User::find($id));
  }

  public function denyFriendRequest(int $id)
  {
    Auth::user()->denyFriendRequest(User::find($id));
  }
}

I tried using the event listener in livewire, but I’m not sure how to deal with the data, when I can just use the Auth::user()->unreadNotifications() inside of my component/

How do I make them realtime ? instead of having to F5 the page.

hi,
This component should be listen for the event, so when the event is fire, you are going to call the render method to fresh the component and there you will have the notification in realtime

  public function getListeners()
  {
    $user_id = Auth::id();

    return ["echo-private:SendFriendRequest.{$user_id},SendFriendRequest" => 'render'];
  }

Added your snipper but it’s still not refreshing, I checked the pusher dashboard and the messages are being sent correctly.

Edit: I changed the render callback to a test function and dd’ing the data inside of that function does contain data, so the listener is working. It’s just not refreshing the component

Hi, i found that you got a problem there

You have a public property called notifications and you are sending also a blade variable with the same name, that cause problem when rendering.

You should use one or the other like this , remove the public property notifications

public function render()
{
$notifications = Auth::user()->unreadNotifications()->get();
return view(‘livewire.notifications’,[‘notifications’ => $notifications]);
}

Hey, this still doesn’t work. After some testing I figured out it might be that I’m sending my friend requests wrong?

  public function sendFriendRequest(int $id)
  {
    $befriendUser = User::where('id', $id)->first();

    if ($befriendUser) {
      Auth::user()->befriend($befriendUser);
      $befriendUser->notify(new SendFriendRequest(Auth::user()));

      event(new \App\Events\SendFriendRequest(Auth::user()));
    }
  }

When I change the livewire listeners into a function and DD the data inside of it, I calls my function when I send the friend request on the user that sent the request, and not the user receiving it. Maybe the channel is configured wrong?

<?php

namespace App\Http\Livewire;

use App\Models\User;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Auth;
use Livewire\Component;

class Notifications extends Component
{

  public function render()
  {
    $notifications = Auth::user()->unreadNotifications()->get();
    return view('livewire.notifications',['notifications' => $notifications]);
  }
  public function getListeners()
  {
    $user_id = Auth::id();

    return ["echo-private:SendFriendRequest.{$user_id},SendFriendRequest" => '$refresh'];
  }

  public function markAsRead(string $id)
  {
    Auth::user()->unreadNotifications()->where('id', $id)->first()->markAsRead();
  }

  public function acceptFriendRequest(int $id)
  {
    Auth::user()->acceptFriendRequest(User::find($id));
  }

  public function denyFriendRequest(int $id)
  {
    Auth::user()->denyFriendRequest(User::find($id));
  }
}
Broadcast::channel('SendFriendRequest.{id}', function ($user, $id) {
  return (int)$user->id === (int)$id;
});

Hi,
Base on your code you are sending the friend request to the same user that is requesting

event(new \App\Events\SendFriendRequest(Auth::user())); //// here you are sending to current user login but should be to the user that you want to send the request.

For example, you are User Id 1 but you are sending a notification to User Id 2, so for the second user to listen for those notifications he will need to listen in SendFriendRequest.2 channel, but you are sending the notification to SendFriendRequest.1

Hey, that seems to fix it. Thank you ! and sorry for asking so many questions

1 Like