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.