Luke Vincent Luke Vincent - 5 months ago 60
PHP Question

Laravel Multiple Model Events

I'm trying to sync my database with an external service.

I'm using Algolia search in a couple of places across a web application.

It's indexed with a couple of models however I need it to re-index in the event that any changes are made to the database i.e. when multiple model events are fired.

My first approach was to action everything within the boot method of the AppServiceProvider

public function boot()
{
$events = ['created', 'updated', 'deleted', 'restored'];

// reindex handlers for models relevant to Algolia search
foreach ($events as $evt) {
Order::registerModelEvent($evt, function () {
Order::reindex();
});
Product::registerModelEvent($evt, function () {
Product::reindex();
Product::setSettings();
});
}
}


This is my approach to avoid multiple conditionals using the standard model functions exampled in the docs.

However I'm assuming there's a better way using Laravel Event Listeners.

namespace App\Listeners;

class OrderEventListener
{
// handlers

public function subscribe($events)
{
$events->listen(
// model events
);
}
}


Although I'm unsure how to tap into the model events in the listen method.

Answer

I would strongly recommend adding your own event and handler for this situation.

In your Product and Order classes you can override the boot method of the model:

class Product extends Model
{
    protected static function boot()
    {
        parent::boot();

        self::created(function($product) {
            event(new ProductCreatedEvent($product));
        });
    }
}

You'll need to create your own ProductCreatedEvent object. Now in the EventServiceProvider you will want to add to the listeners array;

protected $listeners = [
    'App\Events\ProductCreatedEvent' => [
        'App\Listeners\UpdateAlgoliaProductIndex',
    ]
];

Once you've set this up you can actually run php artisan event:generate and this will create the event object and listener for you. I'll skip the event object as it's quite simple it purely takes the product that was created and sends it through to the UpdateAlgoliaProductIndex listener.

Now in your listener you will have something like the following:

class UpdateAlgoliaProductIndex
{
    public function handle($event)
    {
        Product::reindex();
        Product::setSettings();
    }
}

The reason I suggest this approach is you can then queue the listener using the ShouldQueue interface, meaning that you don't block the request whilst waiting for your app to reindex with Algolia leading to a better experience for your users.

You can read more about the event objects and listeners here.

An alternative option is to use a model observer.

Comments