laravel-versionable icon indicating copy to clipboard operation
laravel-versionable copied to clipboard

Revert applies casted data to raw attributes array

Open simonhamp opened this issue 10 months ago • 4 comments
trafficstars

Similar to #84, but basically I found that using setRawAttributes() is just not desired at all. When a model that has been reverted needs to be serialized (e.g. in an API response) it fails because the raw attributes array may contain data in a format that Eloquent doesn't expect.

You need some mechanism to convert the cast data back into its raw type if you're going to use setRawAttributes().

So I found it's better to simply attempt to set the values on the model directly e.g.:

foreach ($this->contents as $key => $value) {
    $this->versionable->$key = $value;
}

I noticed you were using forceFill at one point, which feels like it's the right approach, but I'm not clear why this needed to change.

simonhamp avatar Jan 13 '25 08:01 simonhamp

maybe you can refresh() after revert the model.

overtrue avatar Jan 13 '25 11:01 overtrue

No I can't as I'm using revertWithoutSave() directly as I only want to 'revert' to the version for the life of the request. So I'm not persisting the revert to the DB.

But part of that request requires the model to be serialised, and this fails because of the casted attributes living in the raw attributes.

It's just completely the wrong state for the model to be in.

simonhamp avatar Jan 13 '25 11:01 simonhamp

can you paste an example data ?

overtrue avatar Jan 13 '25 11:01 overtrue

Model with an array cast:

<?php

namespace App\Models;

use Illuminate\Eloquent\Model;

class Test extends Model
{
    protected $casts = [
         'column' => 'array',
    ];
}
$model = Test::make([
    'column' => ['foo' => 'bar'],
]);

dd($model);
// 'attributes' => ['column' => '{"foo": "bar"}']

Note how Eloquent has serialised the data into the model attribute on entry, not on save. Eloquent and other systems (in my case, Inertia) rely on this internal state being a string.

However, your implementation using setRawAttributes() will have this value appear as:

// 'attributes' => ['column' => ['foo' => 'bar']]

Hence the error I'm seeing

simonhamp avatar Jan 20 '25 02:01 simonhamp