Laravel model observer

Laravel model observer

Observe Laravel model events and do some actions.

Laravel Observers are listeners for a model it's a listen-to event method like create, update and delete.

It can be used to modify the model or to modify any other models that are related to the model being saved or deleted, sometimes it can be used to do some actions related to the model.

Let's imagine we have a blog application, in this application, there was a post model and a comment model, and each model contains the following fields:

  • Post

    • user_id

    • title

    • slug

    • cover_image

    • content

    • published_at

    • timestamps(Laravel default)

  • Comment

    • user_id

    • post_id

    • content

    • timestamps(Laravel default)

And we want to do the following:

  • When a post is creating, we want to generate a slug for it.

  • When a post is updating, we want to update the slug if the title is changed.

  • When a post is updating, and the cover photo is changed, we want to delete the old image.

  • When a post is deleted, we want to delete all the comments related to it and delete the cover photo.

We can do this in the controller, but this will make the controller fat, and we will repeat the same code in the update and delete methods, so we can use the observer to do this.

To do this there were two methods:

  • Using the Observer class.

  • Using the Model Events - boot method.

Using the Observer class

Creating the observer

To create an observer we can use the following command:

php artisan make:observer PostObserver --model=Post

This will create a new observer class in the app/Observers directory, and it will be need to registered in the EventServiceProvider class.

<?php

namespace App\Providers;

use App\Models\Post;
use App\Observers\PostObserver;

class EventServiceProvider extends ServiceProvider
{
    /**
     * Register any events for your application.
     */
    public function boot(): void
    {
        Post::observe(PostObserver::class);
    }
}

Alternatively, you may list your observers within an $observers property of your applications EventServiceProvider class

namespace App\Providers;

use App\Models\Post;
use App\Observers\PostObserver;

class EventServiceProvider extends ServiceProvider
{
    /**
     * The model observers for your application.
     *
     * @var array
     */
    protected $observers = [
        Post::class => [PostObserver::class],
    ];

    // ...
}

Creating the observer methods

In our generated observer class we will keep the following methods:

  • creating

  • updating

  • updated

  • deleted

<?php

namespace App\Observers;

use App\Models\Post;

class PostObserver
{
    /**
     * Handle the Post "creating" event.
     */
    public function creating(Post $post): void
    {
        // ...
    }

    /**
     * Handle the Post "updating" event.
     */
    public function updating(Post $post): void
    {
        // ...
    }

    /**
     * Handle the Post "updated" event.
     */
    public function updated(Post $post): void
    {
        // ...
    }

    /**
     * Handle the Post "deleted" event.
     */
    public function deleted(Post $post): void
    {
        // ...
    }
}

Add the logic to the observer methods

Now we can add the logic to the observer methods.

Creating the slug

To create the slug we can use the Str::slug method, and we can use the creating method to do this.


use Illuminate\Support\Str;

/**
 * Handle the Post "creating" event.
 */
public function creating(Post $post): void
{
    $post->slug = Str::slug($post->title);
}

Updating the slug and deleting the old cover photo

To update the slug we can use the Str::slug method, and we can use the updating method to do this, and we can use the isDirty method to check if the title is changed.

To delete the old cover photo we can use the Storage::delete method, and we can use the getOriginal method to get the old cover photo.


use Illuminate\Support\Str;
use Illuminate\Support\Facades\Storage;

/**
 * Handle the Post "updating" event.
 */
public function updating(Post $post): void
{
    /**
     * Update the slug if the title is changed.
     */
    if ($post->isDirty('title')) {
        $post->slug = Str::slug($post->title);
    }

    /**
     * Delete the old cover photo if it's changed.
     */
    if ($post->isDirty('cover_image')) {
        Storage::delete($post->getOriginal('cover_image'));
    }
}

Deleting the comments and the cover photo

To delete the comments we can use the delete method, and we can use the deleted method to do this.

To delete the cover photo we can use the Storage::delete method.


use Illuminate\Support\Facades\Storage;

/**
 * Handle the Post "deleted" event.
 */
public function deleted(Post $post): void
{
    /**
     * Delete the comments related to the post.
     */
    $post->comments()->delete();

    /**
     * Delete the cover photo.
     */
    Storage::delete($post->cover_image);
}

This is the full code of the observer class:

<?php

namespace App\Observers;

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

class PostObserver
{
    /**
     * Handle the Post "creating" event.
     */
    public function creating(Post $post): void
    {
        $post->slug = Str::slug($post->title);
    }

    /**
     * Handle the Post "updating" event.
     */
    public function updating(Post $post): void
    {
        /**
         * Update the slug if the title is changed.
         */
        if ($post->isDirty('title')) {
            $post->slug = Str::slug($post->title);
        }

        /**
         * Delete the old cover photo if it's changed.
         */
        if ($post->isDirty('cover_image')) {
            Storage::delete($post->getOriginal('cover_image'));
        }
    }

    /**
     * Handle the Post "deleted" event.
     */
    public function deleted(Post $post): void
    {
        /**
         * Delete the comments related to the post.
         */
        $post->comments()->delete();

        /**
         * Delete the cover photo.
         */
        Storage::delete($post->cover_image);
    }
}

To learn more about the observer class you can check the official documentation.

Using the Model Events - boot method

Alternatively, you may register the event listeners in the boot method of your model:

<?php

namespace App\Models;

use Illuminate\Support\Str;
use Illuminate\Support\Facades\Storage;

class Post extends Model
{
    /**
     * The "booted" method of the model.
     */
    protected static function booted(): void
    {
        parent::booted();

        /**
         * Handle the Post "creating" event.
         */
        static::creating(function (Post $post) {
            $post->slug = Str::slug($post->title);
        });

        /**
         * Handle the Post "updating" event.
         */
        static::updating(function (Post $post) {
            /**
             * Update the slug if the title is changed.
             */
            if ($post->isDirty('title')) {
                $post->slug = Str::slug($post->title);
            }

            /**
             * Delete the old cover photo if it's changed.
             */
            if ($post->isDirty('cover_image')) {
                Storage::delete($post->getOriginal('cover_image'));
            }
        });

        /**
         * Handle the Post "deleted" event.
         */
        static::deleted(function (Post $post) {
            /**
             * Delete the comments related to the post.
             */
            $post->comments()->delete();

            /**
             * Delete the cover photo.
             */
            Storage::delete($post->cover_image);
        });
    }
}

To learn more about the model events you can check the official documentation.

Conclusion

In this article, we learned how to use the observer class and the model events to do some actions related to the model.

In that case, if you need any help regarding this you can easily contact me from here, I will be happy to help you.

Resources