Incoherence between eloquent `isDirty()` and `getChanges()`

Viewed 1594

I am currently working on a Laravel 5.8 project and when updating a model noticed that even though there aren't any changes to the model I'm saving the same model back into the database.

My thinking to avoid this was the following:

$model = Model::find($id);
$model->fill([
    "name" => $request->name,
    ...
]);
if($model->isDirty){
    $model->save()
}

Problem is that even though I don't change values in my model I'm still entering the if() condition and saving the model. I tried using a temp variable and debugged $model->getChanges() and I get an empty array.

Is this expected behavior?

1 Answers

There is a difference yes.

Related code: https://github.com/laravel/framework/blob/6.x/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php#L1060

isDirty code

/**
 * Determine if the model or any of the given attribute(s) have been modified.
 *
 * @param  array|string|null  $attributes
 * @return bool
 */
public function isDirty($attributes = null)
{
    return $this->hasChanges(
        $this->getDirty(), is_array($attributes) ? $attributes : func_get_args()
    );
}

getChanges() & getDirty code

/**
 * Get the attributes that have been changed since last sync.
 *
 * @return array
 */
public function getDirty()
{
    $dirty = [];
    foreach ($this->getAttributes() as $key => $value) {
        if (! $this->originalIsEquivalent($key, $value)) {
            $dirty[$key] = $value;
        }
    }
    return $dirty;
}
/**
 * Get the attributes that were changed.
 *
 * @return array
 */
public function getChanges()
{
    return $this->changes;
}

To summarize.

Answer used from this post: https://laracasts.com/discuss/channels/eloquent/observer-column-update-isdirty-or-waschanged

isDirty (and getDirty) is used BEFORE save, to see what attributes were changed between the time when it was retrieved from the database and the time of the call, while wasChanged (and getChanges) is used AFTER save, to see that attributes were changed/updated in the last save (from code to the database).

The reason you get in the isDirty check is that before the check you do a fill(). I think this will auto-fill updated_at. So, in fact, the model, in this case, has been changed.

Related