Search code examples
laraveleventseloquentlistenerobservers

How to debug Laravel eloquent event listeners


I'm using Laravel and something is modifying a property before it's saved to the database.

In Database/Eloquent/Model.php I can modify the save() method like so:

dd($this->my_property);
if ($this->fireModelEvent('saving') === false) {
    return false;
}
dd($this->my_property);

In the first dd() the value is what I expected the second it has been modified.

I'm trying to go through Laravel's code but as best I can find it comes from a closure that looks like this when dd()'d:

Closure($event, $payload) {#4282 ▼
  class: "Illuminate\Events\Dispatcher"
  this: Illuminate\Events\Dispatcher {#35 …}
  use: {▼
    $listener: Closure(QueryExecuted $query) {#106 …}
    $wildcard: false
  }
  file: "my/path/vendor/laravel/framework/src/Illuminate/Events/Dispatcher.php"
  line: "453 to 459"
}

This isn't every helpful because Events/Dispatcher.php:453 is just a closure that calls $listener which is also a closure. Trying to debug it any more than that seems really difficult because you wind up catching earlier calls to this method not related to the eloquent update.

Is there any other way to list all listeners and there location to better understand where this property is being modified?

To be specific, I am updating a json array but it always merges the array rather than overwriting it for some reason. At present I have no idea if this is something in my codebase or in Laravel by default.

Example:

// ['one' => 10, 'two' => 20]
dump($model->counts);

$model->update(['counts' => ['three' => 30, 'four' => 40]]);

// ['one' => 10, 'two' => 20, 'three' => 30, 'four' => 40]
dump($model->counts);

// Expected result would be:
// ['three' => 30, 'four' => 40]
dump($model->counts);

So at some point the existing "counts" is being merged with old "counts" rather than overwriting it.


Solution

  • In Laravel 11 there is a command that shows various information about your models and one of the information it provides is the Observers for that model. The command is model:show and you can invoke it running:

    php artisan model:show
    

    In case the Observer section doesn't mention a specific Observer class but it lists Closure then you need to revisit your model class. You could have a closure listed in there or you could have trait that defines such a closure. If it is a local trait you can find it by running a simple grep in the root of your project:

    grep -r creating app
    

    For example running the above command in a project of mine I get the result:

    app/Http/Traits/UsesUuid.php:    static::creating(function ($model) {
    ...
    

    because I use the UsesUuid trait in one of my models.