As your Laravel app grows, repetitive tasks can clutter your controllers and models. Now imagine this: when a customer places an order—...
As your Laravel app grows, repetitive tasks can clutter your controllers and models.
Now imagine this: when a customer places an order—whether from your mobile app, website, or an external API— application should reduce the stock, send a confirmation email, and log the action automatically, no matter where the order came from.
Observers let you automate model event logic in one centralized place—keeping your code clean, consistent, and scalable across your entire application. In this article, I'll discuss:
Laravel Observers are classes that listen to model events and automatically trigger logic - either in the response... or in the background! They help separate event-related logic out of your models and controllers.
For example, in an e-commerce app, when a customer places an order, an observer can:
By using an Observer, it's easy to avoid code duplication and ensure that these tasks are handled automatically.
Laravel supports multiple model event hooks:
Hook | Trigger |
---|---|
retrieved |
After a model is fetched |
creating |
Before a model is created |
created |
After a model is created |
updating |
Before a model is updated |
updated |
After a model is updated |
saving |
Before a model is saved |
saved |
After a model is saved |
deleting |
Before a model is deleted |
deleted |
After a model is deleted |
restoring |
Before a soft-deleted model is restored |
restored |
After a model is restored |
Let's take the same example of an e-commerce application, where on order confirmation, stock is deducted and a confirmation mail is sent with the invoice to the customer.
Let's create the observer file which will observe when an order is confirmed and execute the specific set of actions.
php artisan make:observer OrderObserver --model=Order
Now, let's define the core logic of the Observer, which is easy to understand.
class OrderObserver {
// Triggered when an order is created
public function created(Order $order) {
// Thankyou mail to customer
Mail::to($order->user->email)->send(new OrderThanks($order));
// Order notification to admin
Notification::route('slack', env('SLACK_WEBHOOK'))->notify(new OrderNotification($order));
}
// Triggered when an order status is updated
public function updated(Order $order) {
if ($order->status === 'confirmed' && $order->getOriginal('status') !== 'confirmed') {
// Deduct order item's stock
foreach ($order->products as $product) {
$product->decrement('stock', $product->pivot->quantity);
}
// Send confirmation email
Mail::to($order->user->email)->send(new OrderConfirmation($order));
// Log order confirmation
Log::info("Order confirmed", ['order_id' => $order->id, 'user_id' => $order->user_id]);
}
if ($order->status === 'shipped' && $order->getOriginal('status') !== 'shipped') {
// Send shipment update
Mail::to($order->user->email)->send(new OrderShipment($order));
}
}
}
Note: Never use
request()
inside observers — use model relationships or attributes to get data.
Now, the next step is to register the observer inside AppServiceProvider.php.
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use App\Models\Order;
use App\Observers\OrderObserver;
class AppServiceProvider extends ServiceProvider {
public function boot(): void {
Order::observe(OrderObserver::class);
}
}
Now it will start listening for events on the Order model and handle the predefined actions. Forgetting to register the observer in AppServiceProvider
means it won’t work.
// Create new order
$order = Order::create([
'user_id' => 1,
'status' => 'pending',
'total_amount' => 100
]);
$order->products()->attach([
1 => ['quantity' => 2],
3 => ['quantity' => 1]
]);
// Update order status to confirmed (triggers stock deduction and confirmation email)
$order->update(['status' => 'confirmed']);
// Update order status to shipped (triggers shipment email)
$order->update(['status' => 'shipped']);
When the order status changes to confirmed, OrderObserver listens for the update event on the Order model. It then deducts stock, sends a confirmation email, and logs the event. Similarly, when the status changes to shipped, it sends a shipment notification.
For time-consuming tasks like sending emails, implement ShouldQueue
on your observer to run these tasks in the background.
use Illuminate\Contracts\Queue\ShouldQueue;
class OrderObserver implements ShouldQueue {
// Now runs in the background automatically
}
Make sure to configure your queue workers to process queued jobs properly.
Feature | Observers | Events & Listeners |
---|---|---|
Scope | Model-specific | App-wide |
Decoupling | Tight | Loose |
Use Case | CRUD-related automation | Complex workflows, multiple handlers |
Queuing | Can be queued | Can be queued |
Use Observers for simple, model-tied logic. Use Events/Listeners when the action spans multiple parts of the system.
If you have been handling model related events manually until now - both in the front and and in the backoffice, I recommend you try this way of doing things.💻🙍♂️
Laravel Observers are a great way to keep your models clean, automate common tasks, and improve application structure. Whether you’re building a small app or scaling up to event-driven architecture, they’re a simple and powerful tool to have in your toolbox.
Take initiative, implement Observers — and enjoy writing smarter, cleaner code.
Happy coding!
Subscribe to our "Article Digest". We'll send you a list of the new articles, every week, month or quarter - your choice.
What do you think about this?
Wondering what our community has been up to?