Muhammad Yasir Amin

Loading websiteโ€ฆ

More from Muhammad Yasir Amin

Build Along With Me

Practical walkthroughs on Laravel, React, APIs and deployment workflows.

Subscribe on YouTube

Laravel 13 Model Events and Observers: A Practical Tutorial

๐Ÿ› ๏ธ Laravel Tutorials3 min read

Laravel 13 Model Events and Observers: A Practical Tutorial

Muhammad Yasir Amin

Muhammad Yasir Amin

Senior Full Stack Engineerโ€ขNov 8, 2026
Laravel 13 Model Events and Observers: A Practical Tutorial

Eloquent model events in Laravel 13 fire at points in a model's lifecycle: creating, created, updating, updated, saving, saved, deleting, deleted, restoring and restored. You can listen with closures in the model's booted() method or, for cleaner code, with an observer class generated by php artisan make:observer.

What do you need before you start?

  • Laravel 13 on PHP 8.3 or newer
  • A Post model with title, slug and user_id columns

Which events does Eloquent fire?

  • retrieved: after a model is read from the database
  • creating / created: around the first insert
  • updating / updated: around an update of an existing row
  • saving / saved: around both create and update
  • deleting / deleted: around a delete
  • restoring / restored: for soft-deleted models
  • replicating: when a model is replicated

The "ing" events run before the database write. Returning false from a before-event stops the operation.

How do you listen with closures?

JavaScript
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;

class Post extends Model
{
    protected $fillable = ['title', 'slug', 'user_id'];

    protected static function booted(): void
    {
        static::creating(function (Post $post) {
            $post->slug ??= Str::slug($post->title);
        });

        static::deleted(function (Post $post) {
            logger()->info('Post deleted', ['id' => $post->id]);
        });
    }
}

How do you create an observer?

Code
php artisan make:observer PostObserver --model=Post
JavaScript
<?php

namespace App\Observers;

use App\Models\Post;
use Illuminate\Support\Str;

class PostObserver
{
    public function creating(Post $post): void
    {
        $post->slug ??= Str::slug($post->title);
    }

    public function updated(Post $post): void
    {
        if ($post->wasChanged('title')) {
            logger()->info('Post title changed', ['id' => $post->id]);
        }
    }

    public function deleted(Post $post): void
    {
        // clean up related files, caches, etc.
    }
}

Laravel supports registering the observer with the ObservedBy attribute on the model. You can also call Post::observe(PostObserver::class) in a service provider.

PHP
use App\Observers\PostObserver;
use Illuminate\Database\Eloquent\Attributes\ObservedBy;

#[ObservedBy([PostObserver::class])]
class Post extends Model
{
    // ...
}

When do events NOT fire?

Events fire only when you work through model instances. Bulk queries skip them, because no model is loaded.

JavaScript
Post::where('user_id', 5)->update(['title' => 'x']);   // no updated event
Post::where('user_id', 5)->delete();                     // no deleted event
Post::insert([...]);                                     // no created event
Post::upsert([...], ['slug'], ['title']);               // no events

If you need events, load the models first (for example with each()), accepting the extra queries.

How do you silence events temporarily?

JavaScript
Post::withoutEvents(function () {
    Post::create(['title' => 'Seeded', 'slug' => 'seeded', 'user_id' => 1]);
});

$post->saveQuietly();

Should observers dispatch queued work?

Yes, for slow tasks. Dispatch a job from the observer instead of doing the work inline. If the job depends on committed data, implement ShouldHandleEventsAfterCommit on the observer so it runs after the database transaction commits.

How do you verify it works?

JavaScript
public function test_slug_is_generated_on_create(): void
{
    $post = Post::create(['title' => 'Hello World', 'user_id' => User::factory()->create()->id]);

    $this->assertSame('hello-world', $post->slug);
}

What are the common mistakes?

  • Expecting events from update() on a query builder
  • Heavy work inside saving, which slows every write
  • Observers that touch the model again and loop
  • Forgetting that seeders and factories also trigger events unless silenced

FAQ

Closure listeners or an observer?

Use closures for one or two tiny hooks. Use an observer when logic grows or needs tests.

What is the difference between saving and creating?

saving fires for both inserts and updates. creating fires only for new rows.

How do I stop an operation in a before-event?

Return false from the listener for events such as creating or deleting.

References

Article Index & Topic Tags
LaravelEloquentEventsObservers

YouTube Channel

Build Along With Me

Practical walkthroughs on Laravel, React, APIs and deployment workflows.

Muhammad Yasir Amin
Available for Contracts
Author & Solutions Architect

Senior Full Stack Engineer

Independent Senior Full Stack Engineer working on Laravel, React and Next.js applications, APIs and delivery workflows.

Laravel & PHP ApplicationsReact & Next.js InterfacesREST APIs & IntegrationsDatabase Queries & DebuggingDocker Development WorkflowsClear Scope & Handover
Discuss Your Next Step

What Does Your Application Need Next?

Share your current system, the problem you want to solve and what a useful result looks like. We can identify the right starting point and agree on a practical scope.

Technical Audit

Find the Next Useful Fix

Application Development

Scoped, Reviewable Milestones

Ongoing Support

Agreed Capacity and Priorities

Share your goals, current stack and requirements before the call.