I want to implement email notifications for some events. Also I was this events to be processed asynchronosly using Laravel database queue.
Here is what I have:
Event class:
<?php
namespace App\Events;
use App\Models\ServerReviewVote;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class NewServerReviewVote
{
use Dispatchable, InteractsWithSockets, SerializesModels;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct(public ServerReviewVote $serverReviewVote)
{
}
}
Listener class:
<?php
namespace App\Listeners;
use App\Events\NewServerReviewVote;
use App\Notifications\NewServerReviewVote as NewServerReviewVoteNotification;
use Illuminate\Contracts\Queue\ShouldQueue;
class SendNewServerReviewVoteNotification implements ShouldQueue
{
/**
* Handle the event.
*
* @param NewServerReviewVote $event
* @return void
*/
public function handle(NewServerReviewVote $event)
{
$event->serverReviewVote->serverReview->author
->notify(new NewServerReviewVoteNotification($event->serverReviewVote->serverReview));
}
}
Notification class:
<?php
namespace App\Notifications;
use App\Models\ServerReview;
use App\Models\User;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
class NewServerReviewVote extends Notification implements ShouldQueue
{
use Queueable;
/**
* Create a new notification instance.
*
* @return void
*/
public function __construct(private ServerReview $serverReview)
{
}
/**
* Get the notification's delivery channels.
*
* @param User $notifiable
* @return array
*/
public function via(User $notifiable): array
{
return ['mail'];
}
/**
* Get the mail representation of the notification.
*
* @param User $notifiable
* @return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail(User $notifiable): MailMessage
{
return (new MailMessage)
->subject('New vote for your review!')
->greeting("Hello, {$notifiable->name}!")
->line("We got a new vote for your review for {$this->serverReview->server->name} server!")
->line("Your review currently have {$this->serverReview->votesUpCount()} upvotes and {$this->serverReview->votesDownCount()} downvotes.")
->line("Click the button below to navigate to the server page:")
->action($this->serverReview->server->name, route('servers.show', ['server' => $this->serverReview->server->slug]));
}
}
I'm firing event from this observer:
<?php
namespace App\Observers;
use App\Events\NewServerReviewVote;
use App\Models\ServerReviewVote;
class ServerReviewVoteObserver
{
/**
* @param ServerReviewVote $serverReviewVote
*/
public static function created(ServerReviewVote $serverReviewVote)
{
event(new NewServerReviewVote($serverReviewVote));
}
}
I configured queue database driver and my database has jobs
table.
My expectations is that this event will be added to this table and than I can process it by using php artisan queue:work
. But for some reason email sends synchronously instead of adding to queue. What have I missed?
Ok, I've been trying different ways and found how to make listeners to be added to queue.
I added public $connection = 'database'
property to listener class.
Here is how it looks now:
<?php
namespace App\Listeners;
use App\Events\NewServerReviewVote;
use App\Notifications\NewServerReviewVote as NewServerReviewVoteNotification;
use Illuminate\Contracts\Queue\ShouldQueue;
class SendNewServerReviewVoteNotification implements ShouldQueue
{
/**
* Connection for queue
* @var string
*/
public string $connection = 'database';
/**
* Handle the event.
*
* @param NewServerReviewVote $event
* @return void
*/
public function handle(NewServerReviewVote $event)
{
$event->serverReviewVote->serverReview->author
->notify(new NewServerReviewVoteNotification($event->serverReviewVote->serverReview));
}
}
As far as I understood from Laravel docs this property isn't required but for some reason listener didn't dispatch to queue without it. Now it works good!
Also I cleared notification class as it doesn't need to implement ShouldQueue
interface and use Queueable
trait.
Here is how it looks now:
<?php
namespace App\Notifications;
use App\Models\ServerReview;
use App\Models\User;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
class NewServerReviewVote extends Notification
{
/**
* Create a new notification instance.
*
* @return void
*/
public function __construct(private ServerReview $serverReview)
{
}
/**
* Get the notification's delivery channels.
*
* @param User $notifiable
* @return array
*/
public function via(User $notifiable): array
{
return ['mail'];
}
/**
* Get the mail representation of the notification.
*
* @param User $notifiable
* @return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail(User $notifiable): MailMessage
{
return (new MailMessage)
->subject('New vote for your review!')
->greeting("Hello, {$notifiable->name}!")
->line("We got a new vote for your review for {$this->serverReview->server->name} server!")
->line("Your review currently have {$this->serverReview->votesUpCount()} upvotes and {$this->serverReview->votesDownCount()} downvotes.")
->line("Click the button below to navigate to the server page:")
->action($this->serverReview->server->name, route('servers.show', ['server' => $this->serverReview->server->slug]));
}
}
Now listener successfully dispatching to jobs
table and can be processed by running php artisan queue:work database