Quick Tip: The Convenient Magic of Eloquent Observers

Younes Rafie
Share

If you’ve used Eloquent on medium to large projects before, you may have encountered a situation where you want to take action when something happens to your models. Eloquent provides a convenient way to do so.

Laravel Logo

The Observer Pattern

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. – Wikipedia

In our case, Eloquent models can notify us about changes on a given model.

Model Events

Eloquent provides a handful of useful events to monitor the model state: creating, created, updating, updated, deleting, deleted, saving, saved, restoring, restored.

Notice the “ing/ed” difference.

  • creating: Called before saving the new member.
  • created: Called after saving the member.

Eloquent also fires similar events that we can listen for. The below example attaches a listener to the creating event on the Member model.

Event::listen("eloquent.created: App\\Member", function(Member $member) {
    // do something
});

Creating Observers

Let’s start by creating a new class under the App\Observers namespace and start defining our methods.

// app\Observers\MemberObserver.php

namespace App\Observers;

use App\Member;

class MemberObserver
{
    public function deleting(Member $member) {
        // do something
    }
}

We can use the event name as the method name for each one. We don’t have to define all the methods, just the ones that we want to use.

Every member can subscribe to multiple services, and every service contains many members. Let’s assume we don’t have cascade deletion set up for the associated members_services table, and we need to delete associated services on member deletion to avoid errors when accessing subscribed members for a service.

// app\Observers\MemberObserver.php

namespace App\Observers;

use App\Member;

class MemberObserver
{
    public function deleting(Member $member) {
        $member->services()->delete();
    }
}

Now, the last thing is to attach this observer to the appropriate model. We can do this anywhere we want, but the practical place to put it is inside the app\Providers\AppProvider.php file inside the boot method.

class AppServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        Member::observe("App\\Observers\\MemberObserver");
    }
}

I know that the cascade deletion example was simple and could be done in the controller or directly via MySQL, but this is just a proof of concept.

The good thing about Eloquent observers is that we can abort the current action by returning a false value from the callback method:

class MemberObserver
{
    public function deleting(Member $member) {
        $member->deleted_at = Carbon::now();
        $member->save();

        return false;
    }
}

In the example above, we are soft deleting members and returning false to abort the actual delete action.


Eloquent has many hidden features, and this is one of them. You’ll see this used heavily in big applications and CMSes. If you have any questions or comments about Eloquent, be sure to post them below!