Mark
Mark

Reputation: 1386

Trying to hook into Model 'updating' event with a trait

I'm trying to provide a way to track when a user makes a change to a model for a notes section in my application. E.g. John goes and modifies 2 fields, a note would be created saying John has changed title from 'My title 1' to 'My title 2' and content from 'Lipsum' to 'Lipsum2'.

Here is a trait I created:

<?php

namespace App\Traits;

use Illuminate\Database\Eloquent\Model;

trait TrackChanges
{
    public $changes;

    public static function bootChangesTrait()
    {
        static::updating(function($model)
        {
            $this->changes = [];

            foreach($model->getDirty() as $key => $value)
            {
                $original = $model->getOriginal($key);

                 $this->changes[$key] = [
                    'old' => $original,
                    'new' => $value,
                ];
            }
        });
    }
}

And I am using that trait successfully on my model. However, I'm not sure how to capture the contents of the changes, or if they are even working correctly.

In my controller I have:

$site = Site::findOrFail($id);

// Catch and cast the is_active checkbox if it's been unselected
if ( ! $request->exists('is_active') )
{
    $request->request->add([ 'is_active' => 0 ]);
}

// // Get rid of the CSRF field & method
$data = $request->except([ '_token', '_method' ]);

$site->update($data);

I tried dd($site->changes) before and after $site->update($data); but it just returns null.

What am I doing wrong?

Upvotes: 0

Views: 4178

Answers (1)

Eric Tucker
Eric Tucker

Reputation: 6345

You need to change your boot method in your trait to bootTrackChanges(). To boot traits you need to follow the naming pattern of boot{TraitName} for your boot method. Then you need to change your $this calls in your trait to $model so the change get saved to the model so your trait should look like this:

<?php

namespace App\Traits;

use Illuminate\Database\Eloquent\Model;

trait TrackChanges
{
    public $changes;

    public static function bootTrackChanges()
    {
        static::updating(function($model)
        {
            $changes = [];

            foreach($model->getDirty() as $key => $value)
            {
                $original = $model->getOriginal($key);

                 $changes[$key] = [
                    'old' => $original,
                    'new' => $value,
                ];
            }

            $model->changes = $changes;
        });
    }
}

Another thing to note is if you have defined a boot method in your model make sure you call the parent boot method as well or else your trait's boot methods will not be called and your listener will not be registered.. I have spent hours and hours on this one before due to forgetting to call the parent method. In your model defining a boot method is not required but if you did call the parent like:

class MyModel extends Model
{
    use TrackChanges;

    protected static function boot()
    {
        // Your boot logic here

        parent::boot();
    }
}

Upvotes: 6

Related Questions