Andreyco's avatar

Model events and Observers in Laravel 5.0

Hello folks. I am trying to hook into model events which work fine using model's boot method. I decided to extract desired functionality out of model to its dedicated observer.

The observer is registered in AppServiceProvider.php
Despite the fact Observer is initiated (it's constructor is called, indeed), observer's methods are never called.

public function register()
 {

    // This service provider is a convenient place to register your services
    // in the IoC container. If you wish, you may make additional methods
    // or service providers to keep the code more focused and granular.

    \App\Test::observe(app()->make('App\Observers\TestObserver'));
 }

If I register this service provider right after model class definition (in same file) or in routes file (just to test), it works.

// file: app/Test.php
<?php namespace App;

class Test extends \Illuminate\Database\Eloquent\Model {}

\App\Test::observe(app()->make('App\Observers\TestObserver'));

What am I missing? Is the AppServiceProvider wrong place to register Observers?

Thanks for your help.

0 likes
6 replies
Cocoon's avatar

Does it work when you put this line of code in the boot method of the AppServiceProvider? I think it should go in there...

1 like
Andreyco's avatar

I forgot to mention I tried to register model's observer in boot method as well. Same result. Going to dig deeper in the code...

lara8818's avatar
Level 1

I've also been struggling with this issue of where to put Model Observers and the lack of updated documentation (while totally understandable as Laravel 5 isn't even finished) is frustrating. Here are my findings...

When you call the observe() method on an Eloquent model it registers the given Observer object to the registerModelEvent() method. This methods contents can be seen below:

// Pulled from Illuminate\Database\Eloquent\Model

/**
  * Register a model event with the dispatcher.
  *
  * @param string $event
  * @param \Closure|string $callback
  * @return void
  */
 protected static function registerModelEvent($event, $callback)
 {
  if (isset(static::$dispatcher))
  {
   $name = get_called_class();

   static::$dispatcher->listen("eloquent.{$event}: {$name}", $callback);
  }
 }

The operator if (isset(static::$dispatcher)) immediately caught my eye and I realized the source of the issue is likely due to the event dispatcher object not being set. Long story short, the event dispatcher is attached to our base Model class in the DatabaseServiceProvider.

class DatabaseServiceProvider extends ServiceProvider {

 /**
  * Bootstrap the application events.
  *
  * @return void
  */
 public function boot()
 {
  Model::setConnectionResolver($this->app['db']);

  Model::setEventDispatcher($this->app['events']);
 }

The resolution I found was to create an ObserverServiceProvider and place it after DatabaseServiceProvider in the config/app.php file.

<?php namespace App\Providers;

use Illuminate\Support\ServiceProvider;

class ObserverServiceProvider extends ServiceProvider
{

  /**
   * Bootstrap any necessary services.
   *
   * @return void
   */
  public function boot()
  {
    \App\User::observe( new \App\Observers\UserObserver );
  }

  /**
   * Register the service provider.
   *
   * @return void
   */
  public function register()
  {
  }

}

Another solution would be to extend DatabaseServiceProvider and replace the original in config/app.php. You could even add a new config option to config/database.php called observers that would accept an associative array of ModelClass => ObserverClass and have this array be looped over and handled in a custom version of DatabaseServiceProvider.

8 likes
pveyes's avatar

Stumbled upon this,

I put my observer in App\Providers\EventServiceProvider in boot method. And the next step is very important:

Run php artisan optimize after modifying service provider.

Hope that helps

digitalagua's avatar

I came across this issue and the solution provided seemed complex. You can put the observe in the boot of the observed model.

class User extends Model
{
    public static function boot()
    {
        parent::boot();

        User::observe(new UserObserver);
    }
    
    ...
}

Please or to participate in this conversation.