Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[8.x] Fix getDirty method when using AsArrayObject / AsCollection #38869

Conversation

gdebrauwer
Copy link
Contributor

A less impactful version of #38774.

I updated the originalIsEquivalent method to make a custom comparison when the attribute uses AsArrayObject or AsCollection cast. This way, Eloquent will not think there is a difference if there are just some extra spaces in the original json string.

// If you try to save this in a MySQL json column:
// {"foo":"bar"}
// Then MySQL will save it like this:
// {"foo": "bar"}

// This means that Eloquent currently thinks that a json value is changed
// because there is an extra space in the original value.
'{"foo": "bar"}' !== '{"foo":"bar"}'

@taylorotwell
Copy link
Member

@gdebrauwer can you revert all of those unrelated "$value" changes in the other cast files?

@gdebrauwer
Copy link
Contributor Author

Those changes are necessary, otherwise, the wrong value will be cast. The castable class will just be casting the model's current value instead of the provided value

@taylorotwell
Copy link
Member

@gdebrauwer In what situations would $value and $attributes[$key] be different values?

@gdebrauwer
Copy link
Contributor Author

When you try to cast the original value, instead of the updated value

@taylorotwell
Copy link
Member

@gdebrauwer is there a way to do this without recasting? Like just json_encode(json_decode($foo)) so they are normalized? I still find it odd to pass the original attribute and the current attribute array in the same cast call.

@gdebrauwer
Copy link
Contributor Author

Yeah that would work. I'll have a look tomorrow and update the PR 👌

(regarding the attributes-array, honestly, I don't really know why that fourth parameter exists 😅)

@taylorotwell
Copy link
Member

@gdebrauwer thanks

@gdebrauwer
Copy link
Contributor Author

@taylorotwell It now uses the existing fromJson method to cast the values to arrays so those arrays can be compared

@taylorotwell
Copy link
Member

@gdebrauwer

Are the $value changes in the cast files now able to be reverted?

@gdebrauwer
Copy link
Contributor Author

Forgot about those 😅 I reverted those changes.

@taylorotwell
Copy link
Member

Do you know if these changes work with AsEncryptedArrayObject and AsEncryptedCollection?

@gdebrauwer
Copy link
Contributor Author

Those do not have the same issue, as the encrypted value will be saved in a string column instead of json column

@taylorotwell taylorotwell merged commit 6216534 into laravel:8.x Sep 22, 2021
@SachinAgarwal1337
Copy link

@taylorotwell @gdebrauwer

This is breaking the default attributes logic.
I have my default attributes set like this

protected $attributes = [
        'settings' => [
            'is_public' => true,
            'footer' => true,
        ],
    ];

and casting like this

 /**
     * @var array
     */
    protected $casts = [
        'settings' => AsArrayObject::class,
    ];

Before this update everything was working fine.
After update my tests start to break. Giving the following error
Screenshot 2021-10-01 at 17 23 37

@gdebrauwer
Copy link
Contributor Author

I think you have to provide a json-encoded string as default value:

protected $attributes = [
    'settings' => '{"is_public":true,"footer":true}'
];

The error is not caused by this change, because if you use the 'object' cast on that attribute for example, then you will have the same issue.

protected $attributes = [
    'settings' => [
        'is_public' => true,
        'footer' => true,
    ],
];

protected $casts = [
    'settings' => 'object'
];

// this setup will also cause the json_decode error

The $attributes array should contain database-like values, which arrays are not.

@SachinAgarwal1337
Copy link

@gdebrauwer hmm... Interesting.
But I can assure you it's working that way befor I updated to the version this change got released.
(I tested by downgrading the version)

I can update it to that json string.
But isn't this a breaking change?

Also I think the way it was working earlier seems right to me.
It was applying the casting on default values defined in $attributes
Something to think about maybe?

@SachinAgarwal1337
Copy link

Also as you can see that column name is settings so its highly possible there may be like 10-15 keys there for settings. It wont be ideal to add the default value as json string. That will make it more difficult to maintain.
Again something to think about maybe?

@SachinAgarwal1337
Copy link

@taylorotwell hey, what do think about my comments? Any thoughts?

victorvilella pushed a commit to cdsistemas/framework that referenced this pull request Oct 12, 2021
…laravel#38869)

* Add failing tests

* Fix casts

Use $value instead of $attributes[$key]

* Fix originalIsEquivalent method

* Fix linting issue

* Compare json values without recasting

* Revert cast changes
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants