Laravel Events & Listeners with Queue Tutorial

Laravel Events and Listeners

Let’s create Events and Listeners in Laravel and use Queue and Task Scheduling features to process them in the background without affecting the speed of our application.

Understanding Events and Listeners In Laravel.

Laravel provides an observer pattern implementation that can watch an event in our web application and process actions according to that event. Let’s say, if a user registers, we send a welcome email. so that the registration is the Event and the Listener is the action of sending the welcome email. In other words, Laravel Event classes observe and Listeners classes do the actions.

One Event can have many Listeners for example when the user registers, we can send a welcome email, or send verification email. Another Event is when the user purchases an order we create a listener for sending a confirmation email with the invoice and another listener to decrease the stock in our database and we can create one more Listener to check if an order’s item becomes best-selling or not.

Why should we use Laravel Events and Listeners?

It will keep our code clean following solid principles (wink wink) and easy to reuse and maintain since it’s already existed in Laravel documentation and easy to work with queues. I believe that it’s one good observer pattern.

Let’s create Events and Listeners

We will create an Event class to observe the user login and a Listener for sending a Loggedin email notification. So that when the user login we send an email.

First thing, first let’s create our Event. By default, Laravel will create our class file in app\Events

php artisan make:event LoggedIn

Let’s create our Listener and assign it to LoggedIn Event, as the following.

php artisan make:listener SendLoginNotification  --event=LoggedIn

By default, Laravel will create our class file in app\Listeners, we need to register our Event and Listener in app\Providers\EventServiceProvider.php so that Laravel can know about our classes

<?php

namespace App\Providers;

use Illuminate\Auth\Events\Registered;
use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Event;
use App\Events\LoggedIn;
use App\Listeners\SendLoginNotification;

class EventServiceProvider extends ServiceProvider
{
  
    protected $listen = [
        Registered::class => [
            SendEmailVerificationNotification::class,
        ],
        LoggedIn::class => [
            SendLoginNotification::class,
        ],
    ];


    public function boot()
    {
        //
    }

  
    public function shouldDiscoverEvents()
    {
        return false;
    }
}

Here In EventServiceProvider we have $listen array the index is the event class name and the value is an array with all the subscribed listener classes of that event.

I made the below example with some possible events and listeners. Once the event is triggered all its listeners will be processed.

protected $listen = [
        Registered::class => [
            SendWelcomeMail::class,
            SendEmailVerificationNotification::class,
        ],
        LoggedIn::class => [
            SendLoginNotification::class,
        ],
        NewOrder::class => [
            ConfirmOrderNotification::class,
            DecreaseStock::class,
            SendingOrderDataToTheStaff::class,
        ],
    ];

Let’s open app\Events\LoggedIn.php so that we can add a user model instance that we can pass to our listeners. The class will have only __construct

<?php

namespace App\Events;

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;
use App\Models\User;
class LoggedIn
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public $user;
  
    public function __construct(User $user)
    {
        $this->user = $user;
    }

 
}

Let’s code our app\Listeners\SendLoginNotification.php class to send the notification mail.

<?php

namespace App\Listeners;

use App\Events\LoggedIn;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use App\Notifications\LoginNotification;

class SendLoginNotification implements ShouldQueue
{
    use InteractsWithQueue;

    public $tries = 5;
  
    public function __construct()
    {
        //
    }

   
    public function handle(LoggedIn $event)
    {
          try{
            $event->user->notify(new LoginNotification());

             //more codes for other things
           }catch(\Exception $e){
            //
            }
    }
}
  • If you don’t want to add the Listener to the queue remove implements ShouldQueue use InteractsWithQueue; public $tries = 5; so that It will be executed with the request.
  • We can call the user model in handle function from $event parameter as the following $event->user.
  • LoginNotification() that is a normal notification class, I made a youtube video about it here Laravel 9: How To Send Email Notifications in Laravel.

To use the Event, we can dispatch it at the login function in the authentication controller as the following.

public function login (LoginRequest $request){

         //

          if(auth()->attempt($credentials )){
            $user = Auth::user();

             //

            LoggedIn::dispatch($user);
            //
            return response()->json( $success,200);
            
          }else{
            //
          }

    }

Configuring Laravel Queue

When we used ShouldQueue in app\Listeners\SendLoginNotification.php, we added the listener to the queue. But we need to make some more steps for configuration.

Database Queue

We need to create a Jobs table in the database to save the queue. Laravel has it out of the box.

php artisan queue:table
 
php artisan migrate

In .env file, we make the queue connection database.

QUEUE_CONNECTION=database

If you tried to log in you should see the Job record added to the Jobs table in the database.

Redis queue

You read that, I see, you like Redis, here is more Redis: Laravel Cache Redis And File Store Tutorial With Example. we will use the same configuration as that tutorial.

Let’s install predis/predis package, the flexible and feature-complete Redis client for PHP and Laravel.

composer require predis/predis

And make sure the configuration for the Redis client is 'predis' in config\database.php. I’m using Windows 10 with WSL2 and Ubuntu 22, that’s my configuration.

'redis' => [
       
        'client' => env('REDIS_CLIENT', 'predis'),

        'options' => [
            'cluster' => env('REDIS_CLUSTER', 'redis'),
            'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'),
        ],

        'default' => [
            'url' => env('REDIS_URL'),
            'host' => env('REDIS_HOST', '127.0.0.1'),
            'username' => env('REDIS_USERNAME'),
            'password' => env('REDIS_PASSWORD'),
            'port' => env('REDIS_PORT', '6379'),
            'database' => env('REDIS_DB', '0'),
        ],

        'cache' => [
            'url' => env('REDIS_URL'),
            'host' => env('REDIS_HOST', '127.0.0.1'),
            'username' => env('REDIS_USERNAME'),
            'password' => env('REDIS_PASSWORD'),
            'port' => env('REDIS_PORT', '6379'),
            'database' => env('REDIS_CACHE_DB', '1'),
        ],

    ],

And make sure .env we use Redis as our queue connection driver.

QUEUE_CONNECTION=redis

If you tried to log in the queue job should be saved in Redis.

Starting the queue worker

We added our listener to the queue in the database or Redis. to start the queue, we use the below artisan command.

 php artisan queue:work 

To make the queue work automatically in the background we can add it to the Laravel task scheduling as in app\Console\Kernel.php

 //
 protected function schedule(Schedule $schedule)
    {
       
        $schedule->command('queue:work')->everyMinute();
    }

//

Finally, you need to set up a cron job in your server for Laravel but in development, you can use this artisan command to run the schedule.

php artisan schedule:run

I hope this tutorial was useful for you, thank you.

Leave a Reply

Your email address will not be published. Required fields are marked *