Search code examples
eventsnotificationsqueuelistenerlaravel-8

Laravel 8.24 listeners/events doesn't adds to queue


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?


Solution

  • 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