How to Use Observers in Laravel

Observers are php classes that we can use to listen to events on an eloquent model.

every eloquent model dispatches several events.

for instance when we create or save a model ($product->save()) “created” and “creating” events are dispatched. we use observers to do an operation when these events are dispatched.

here is a list of all the event models:

  • retrieved
  • creating
  • created
  • updating
  • updated
  • saving
  • saved
  • deleting
  • deleted
  • trashed
  • forceDeleting
  • forceDeleted
  • restoring
  • restored
  • replicating.

the events that end wth -ing are dispatched before the eloquent model changes and events that end with ed are dispatched after the model is changed.

for example, “creating” is dispatched before the model is created and “created dipatches after a model is created.

How to use Observers

first we need to create an observer class for our model
use make:observer command:

php artisan make:observer OrderObserver --model=Order

the above command will create Orderobserver.php in app\Observers:

OrderObserver class has 5 methods that represent the events. you can add other methods like creating or saving if you need them.

<?php

namespace App\Observers;

use App\Models\Order;

class OrderObserver
{

    public function created(Order $order)
    {
        //
    }


    public function updated(Order $order)
    {
        //
    }


    public function deleted(Order $order)
    {
        //
    }


    public function restored(Order $order)
    {
        //
    }


    public function forceDeleted(Order $order)
    {
        //
    }
}

I use the “created” method in this example to notify the manager when an order is created.


public function created(Order $order)
    {
        $manager = User::where('role',',manager')->first();

        Mail::to($manager)->send(new OrderCreated($order));
    }

this observer doesn’t work until you register it in the boot method of EventServiceProvider class.

in App\Providers\EventServiceProvider:

namespace App\Providers;

use App\Models\Order;
use App\Observers\OrderObserver;

class EventServiceProvider extends ServiceProvider
{

 public function boot()
    {
        Order::observe(OrderObserver::class);
    }
}

You also can put all of your observers in an $observers property in EventServiceProvider:

//EventServiceProvider.php

use App\Models\User;
use App\Observers\UserObserver;
use App\Models\Order;
use App\Observers\OrderObserver;

//...

 protected $observers = [
        Order::class => [OrderObserver::class],
        User::class  => [UserObserver::class],
    ];

Observing Database Transactions

Sometimes we have a “created” event for a model like User or Order and that happens in a database transaction alongside other operations.

if we want to run the observer code after the database transaction is finished, we have to implement ShouldHandleEventsAfterCommit in observer class

like this:

<?php

namespace App\Observers;

use App\Mail\OrderCreated;
use App\Models\Order;
use App\Models\User;
use Illuminate\Support\Facades\Mail;
use Illuminate\Contracts\Events\ShouldHandleEventsAfterCommit;

class OrderObserver implements ShouldHandleEventsAfterCommit
{




}

mute model events

To mute all events dispatched by a model we use the withoutEvents() method.

this method accepts a closure. we have to write the code in this closure:

$orderModel = Order::withoutEvents(function(){
  
             $order = Order::create($orderData);
             return $order

        });

everything we return in the closure will be returned by withoutEvents method. in the above code $order is returned by the closure function so $orderModel will have the $order value.

Example From An Open Source Software

take a look at this Observer class from the Invoiceninja repository in Github.

it has an observer called TaskObserver.

in the TaskObserver class, there is a method named “updated”.

    public function updated(Task $task)
    {
        $event = Webhook::EVENT_UPDATE_TASK;

        if ($task->getOriginal('deleted_at') && !$task->deleted_at) {
            $event = Webhook::EVENT_RESTORE_TASK;
        }
        
        if ($task->is_deleted) {
            $event = Webhook::EVENT_DELETE_TASK;
        }
        
        
        $subscriptions = Webhook::where('company_id', $task->company_id)
                                    ->where('event_id', $event)
                                    ->exists();

        if ($subscriptions) {
            WebhookHandler::dispatch($event, $task, $task->company)->delay(0);
        }
    }

this method runs after a task is updated. it checks if the task is soft deleted or not and then fires the appropriate event based on that.

Observers are a good way to attach an operation to a model event and help us have cleaner controllers. but we have to remember when working in a team or on a large project, using observers can cause issues and side effects especially in testing.

this article explains this issue with an example.

Similar Posts

Leave a Reply

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