laraveleventseloquentevent-handlinglaravel-9

Ignore the updated event when an Eloquent model is restored


I am building a package that logs changes that happen on eloquent and I'm trying to figure out what to check to ignore the updated event when restored.

trait HasLogs
{
    public static function bootHasLogs(): void
    {
        // created ...
        self::updated(callback: fn ($model) => self::log($model, 'updated'));

        self::deleted(callback: function ($model) {
            // Ignore 'deleted' event when 'softDeleted' or 'forceDeleted'
            if (in_array('Illuminate\Database\Eloquent\SoftDeletes', (class_uses(self::class)))) {
                return;
            }
            self::log($model, 'deleted');
        });

        if (in_array('Illuminate\Database\Eloquent\SoftDeletes', (class_uses(self::class)))) {
            // softDeleted ...
            self::restored(callback: fn ($model) => self::log($model, 'restored'));
            // forceDeleted ...
        }
    }
    // ...
}

Here is the actions order if that helps:

  1. Model::restore() on the SoftDeletes trait.
  2. ---- restoring is fired.
  3. ---- Model::save()
  4. -------- saving is fired.
  5. -------- Model::performUpdate()
  6. ------------ updating is fired.
  7. ------------ Builder::update()
  8. ------------ updated is fired.
  9. -------- Model::finishSave()
  10. ------------ saved is fired.
  11. ---- restored is fired.

Solution

  • The trait HasLogs adds a Boolean attribute to the Model named loggableIsBeingRestored set to false by default.

    The trait also registers a new listener for restoring which sets loggableIsBeingRestored to true at the begging of the described actions.

    Then the updated listener checks for loggableIsBeingRestored before proceeding with its actions.

    trait HasLogs
    {
        public bool $loggableIsBeingRestored = false;
    
        public static function bootHasLogs(): void
        {
            // ...
    
            self::updated(callback: function ($model) {
                if (isset($model->loggableIsBeingRestored) && $model->loggableIsBeingRestored) {
                    // This is a restored event so don't log!
                    return;
                }
    
                self::log($model, 'updated');
            });
    
            // ...
    
            self::restored(callback: function ($model) {
                self::log($model, 'restored');
    
                $model->loggableIsBeingRestored = false;
            });
    
            self::restoring(callback: function ($model) {
                $model->loggableIsBeingRestored = true;
            });
        }
        // ...
    }