How To Use Laravel Model Observers

Laravel’s Eloquent ORM is the rock-solid implementation of Active Record. Apart from other awesome features offered by Laravel Eloquent, Laravel implements Observer Pattern to fire some events, which can be listened to hook into, when various actions are performed on a model.

The observer pattern is a software design pattern in which an object, called the subject, maintains a list of its dependents, called observers, and notifies them automatically of any state changes, usually by calling one of their methods. Source : Wikipedia

In this post, we will be learning about Laravel’s model events and how we can create model observers for grouping event listeners.

Laravel Model Events

If you have used Laravel for a medium to large scale project, you might have encountered a situation where you want to perform some action while your Eloquent model is processing. Laravel’s Eloquent provide a convenient way to add your own action while the model is completing or has completed some action.

For example, if you have Post model and you want to set post slug automatically when a post is created. You can listen to the saving event and set the post slug like below:

namespace App;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
    protected $table = 'posts';
    protected $fillable = ['title', 'slug', 'content'];
    protected static function boot()
    {
        parent::boot();
        static::saving(function ($model) {
            $model->slug = str_slug($model->title);
        });
    }
}

Eloquent provides a handful of events to monitor the model state which are:

  • retrieved : after a record has been retrieved.
  • creating : before a record has been created.
  • created : after a record has been created.
  • updating : before a record is updated.
  • updated : after a record has been updated.
  • saving : before a record is saved (either created or updated).
  • saved : after a record has been saved (either created or updated).
  • deleting : before a record is deleted or soft-deleted.
  • deleted : after a record has been deleted or soft-deleted.
  • restoring : before a soft-deleted record is going to be restored.
  • restored : after a soft-deleted record has been restored.

Laravel Model Observers

Imagine your application is growing, and you have to listen to most of the above events in your model, listening to the events within the model will make your model very big and messy of course.

Using model observers, you can group all your events into a single class. All method names in the observer class will reflect on the event you are listening to. You can create a model observer class using below artisan command.

php artisan make:observer PostObserver --model=Post

Above command will create a new class located in the app/Observers folder. Your newly created observer class will look like below:

namespace App\Observers;
use App\Post;
class PostObserver
{
    /**
     * Handle the post "created" event.
     *
     * @param  \App\Post  $post
     * @return void
     */
    public function created(Post $post)
    {
        //
    }
    /**
     * Handle the post "updated" event.
     *
     * @param  \App\Post  $post
     * @return void
     */
    public function updated(Post $post)
    {
        //
    }
    /**
     * Handle the post "deleted" event.
     *
     * @param  \App\Post  $post
     * @return void
     */
    public function deleted(Post $post)
    {
        //
    }
    /**
     * Handle the post "restored" event.
     *
     * @param  \App\Post  $post
     * @return void
     */
    public function restored(Post $post)
    {
        //
    }
    /**
     * Handle the post "force deleted" event.
     *
     * @param  \App\Post  $post
     * @return void
     */
    public function forceDeleted(Post $post)
    {
        //
    }
}

Now you have to register this class in Laravel’s Service Container, so we will add this class in AppServiceProvider‘s boot() method by telling Post model to observe the PostObserver class, like below:

namespace App\Providers;
use App\Post;
use App\Observers\PostObserver;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        //
    }
    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        Post::observe(PostObserver::class);
    }
}

In our PostObserver class, Laravel created some function by default like created, updated and others. We will delete all of them and add a saving hook by telling to create a post slug from post title before the post is saved.

Replace your observer class with below.

namespace App\Observers;
use App\Post;
class PostObserver
{
    /**
     * Handle the post "saving" event.
     *
     * @param  \App\Post  $post
     * @return void
     */
    public function saving(Post $post)
    {
        $post->slug = str_slug($post->title);
    }
}

Now we have successfully added a model observer to observe our Post model, whenever you create a new post, it’s slug will be automatically created before saving the post.

Practicle Example

For example, our Post model has associated comments. What will happen if I delete a post, will that delete the comments as well?

Of Course Not

Obviously, we don’t want to delete the post comments manually in our code base where we are deleting the post. We can use the deleting hook to do this for us.

You might will have a HasMany relationship to the Comment model and you model will look like below.

namespace App;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
    protected $table = 'posts';
    protected $fillable = ['title', 'slug', 'content'];
    public function comments()
    {
        return $this->hasMany(Comment::class);
    }
}

Now we can add a deleting hook in our PostObserver class like below and delete the attached comments in it.

namespace App\Observers;
use App\Post;
class PostObserver
{
    /**
     * Handle the post "saving" event.
     *
     * @param  \App\Post  $post
     * @return void
     */
    public function saving(Post $post)
    {
        $post->slug = str_slug($post->title);
    }
    /**
     * Handle the post "deleting" event.
     *
     * @param  \App\Post  $post
     * @return void
     */
    public function deleting(Post $post)
    {
        $post->comments()->delete();
    }
}

Now every time, you delete a post, all comments associated with that post will get deleted.

Limitations

There are some limitations attached to Model Observer which you should keep in mind while using the model observers.

  • When you use saved or saving hooks, you should never call the model’s save method.
  • If you are using saved hook and want to call the save method, then you should probably use the saving hook.
  • If your logic need to call model’s save method, then rethink your logic or avoid using observers.

Conclusion

Laravel Model Observers are a very powerful yet useful feature and can make your codebase more maintainable. You should keep in mind about the limitations and where you need to apply observers. My formula for using observers is to apply as minimal logic in the observer as I can.