sajad torkamani

In a nutshell

Laravel’s event system provides a simple observer pattern implementation, allowing you to subscribe to various events that occur within your application and to take appropriate actions as needed.

Event classes are typically stored in the app/Events directory, while their listeners are stored in app/Listeners

Why use events?

Events serve as a great way to decouple various aspects of your application, since a single event can have multiple listeners that do not depend on each other. For example, you may wish to send a Slack notification to your user each time an order has shipped. Instead of coupling your order processing code to your Slack notification code, you can raise an App\Events\OrderShipped event which a listener can receive and use to dispatch a Slack notification.

Make event

php artisan make:event

Make listener

php artisan make:listener

Register events & listeners

Auto discovery

By default, Laravel will automatically find and register your event listeners by scanning your application’s Listeners directory. When Laravel finds any listener class method that begins with handle or __invoke, Laravel will register those methods as event listeners for the event that is type-hinted in the method’s signature:

use App\Events\PodcastProcessed;
 
class SendPodcastNotification
{
    /**
     * Handle the given event.
     */
    public function handle(PodcastProcessed $event): void
    {
        // ...
    }
}

Manually register events

Using the Event facade, you may manually register events and their corresponding listeners within the boot method of your application’s AppServiceProvider:

use App\Domain\Orders\Events\PodcastProcessed;
use App\Domain\Orders\Listeners\SendPodcastNotification;
use Illuminate\Support\Facades\Event;
 
/**
 * Bootstrap any application services.
 */
public function boot(): void
{
    Event::listen(
        PodcastProcessed::class,
        SendPodcastNotification::class,
    );
}

List app events

php artisan event:list

Queue an event listener

To specify that a listener should be queued, add the ShouldQueue interface to the listener class. Listeners generated by the make:listener Artisan commands already have this interface imported into the current namespace so you can use it immediately:

<?php
 
namespace App\Listeners;
 
use App\Events\OrderShipped;
use Illuminate\Contracts\Queue\ShouldQueue;
 
class SendShipmentNotification implements ShouldQueue
{
    // ...
}

That’s it! Now, when an event handled by this listener is dispatched, the listener will automatically be queued by the event dispatcher using Laravel’s queue system. If no exceptions are thrown when the listener is executed by the queue, the queued job will automatically be deleted after it has finished processing.

Customizing The Queue Connection, Name, & Delay

<?php
 
namespace App\Listeners;
 
use App\Events\OrderShipped;
use Illuminate\Contracts\Queue\ShouldQueue;
 
class SendShipmentNotification implements ShouldQueue
{
    /**
     * The name of the connection the job should be sent to.
     *
     * @var string|null
     */
    public $connection = 'sqs';
 
    /**
     * The name of the queue the job should be sent to.
     *
     * @var string|null
     */
    public $queue = 'listeners';
 
    /**
     * The time (seconds) before the job should be processed.
     *
     * @var int
     */
    public $delay = 60;
}

Conditionally Queueing Listeners

Sometimes, you may need to determine whether a listener should be queued based on some data that are only available at runtime. To accomplish this, a shouldQueue method may be added to a listener to determine whether the listener should be queued. If the shouldQueue method returns false, the listener will not be queued:

<?php
 
namespace App\Listeners;
 
use App\Events\OrderCreated;
use Illuminate\Contracts\Queue\ShouldQueue;
 
class RewardGiftCard implements ShouldQueue
{
    /**
     * Reward a gift card to the customer.
     */
    public function handle(OrderCreated $event): void
    {
        // ...
    }
 
    /**
     * Determine whether the listener should be queued.
     */
    public function shouldQueue(OrderCreated $event): bool
    {
        return $event->order->subtotal >= 5000;
    }
}

Queued Event Listeners and Database Transactions

See here.

Handle failed jobs

Sometimes your queued event listeners may fail. If the queued listener exceeds the maximum number of attempts as defined by your queue worker, the failed method will be called on your listener. The failed method receives the event instance and the Throwable that caused the failure:

<?php
 
namespace App\Listeners;
 
use App\Events\OrderShipped;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Throwable;
 
class SendShipmentNotification implements ShouldQueue
{
    use InteractsWithQueue;
 
    /**
     * Handle the event.
     */
    public function handle(OrderShipped $event): void
    {
        // ...
    }
 
    /**
     * Handle a job failure.
     */
    public function failed(OrderShipped $event, Throwable $exception): void
    {
        // ...
    }
}

Specify maximum attempts for queued listener

<?php
 
namespace App\Listeners;
 
use App\Events\OrderShipped;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
 
class SendShipmentNotification implements ShouldQueue
{
    use InteractsWithQueue;
 
    /**
     * The number of times the queued listener may be attempted.
     *
     * @var int
     */
    public $tries = 5;
}

Specify maximum time duration for queued listener

use DateTime;
 
/**
 * Determine the time at which the listener should timeout.
 */
public function retryUntil(): DateTime
{
    return now()->addMinutes(5);
}

Dispatch event

To dispatch an event, you may call the static dispatch method on the event. This method is made available on the event by the Illuminate\Foundation\Events\Dispatchable trait. Any arguments passed to the dispatch method will be passed to the event’s constructor:

<?php
 
namespace App\Http\Controllers;
 
use App\Events\OrderShipped;
use App\Http\Controllers\Controller;
use App\Models\Order;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
 
class OrderShipmentController extends Controller
{
    /**
     * Ship the given order.
     */
    public function store(Request $request): RedirectResponse
    {
        $order = Order::findOrFail($request->order_id);
 
        // Order shipment logic...
 
        OrderShipped::dispatch($order);
 
        return redirect('/orders');
    }
}

Ensure events are dispatched after database transactions have committed

Sometimes, you may want to instruct Laravel to only dispatch an event after the active database transaction has committed. To do so, you may implement the ShouldDispatchAfterCommit interface on the event class.

This interface instructs Laravel to not dispatch the event until the current database transaction is committed. If the transaction fails, the event will be discarded. If no database transaction is in progress when the event is dispatched, the event will be dispatched immediately:

<?php
 
namespace App\Events;
 
use App\Models\Order;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Events\ShouldDispatchAfterCommit;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
 
class OrderShipped implements ShouldDispatchAfterCommit
{
    use Dispatchable, InteractsWithSockets, SerializesModels;
 
    /**
     * Create a new event instance.
     */
    public function __construct(
        public Order $order,
    ) {}
}

Write an Event Subscriber

Event subscribers are classes that may subscribe to multiple events from within the subscriber class itself, allowing you to define several event handlers within a single class. Subscribers should define a subscribe method, which will be passed an event dispatcher instance. You may call the listen method on the given dispatcher to register event listeners:

<?php
 
namespace App\Listeners;
 
use Illuminate\Auth\Events\Login;
use Illuminate\Auth\Events\Logout;
use Illuminate\Events\Dispatcher;
 
class UserEventSubscriber
{
    /**
     * Handle user login events.
     */
    public function handleUserLogin(Login $event): void {}
 
    /**
     * Handle user logout events.
     */
    public function handleUserLogout(Logout $event): void {}
 
    /**
     * Register the listeners for the subscriber.
     */
    public function subscribe(Dispatcher $events): void
    {
        $events->listen(
            Login::class,
            [UserEventSubscriber::class, 'handleUserLogin']
        );
 
        $events->listen(
            Logout::class,
            [UserEventSubscriber::class, 'handleUserLogout']
        );
    }
}

Register Event Subscribers

After writing the subscriber, Laravel will automatically register handler methods within the subscriber if they follow Laravel’s event discovery conventions. Otherwise, you may manually register your subscriber using the subscribe method of the Event facade. Typically, this should be done within the boot method of your application’s AppServiceProvider:

<?php
 
namespace App\Providers;
 
use App\Listeners\UserEventSubscriber;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\ServiceProvider;
 
class AppServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     */
    public function boot(): void
    {
        Event::subscribe(UserEventSubscriber::class);
    }
}
Tagged: Laravel