Skip to content

Conversation

@cosmastech
Copy link
Contributor

@cosmastech cosmastech commented Nov 15, 2025

This fix stops double quoting JSON arrays when using Factory@insert().


Thanks to @zepfietje for reporting this issue and providing reproduction steps. 🙇 #57600 (comment)

@riesjart You had mentioned there were multiple reasons for not using $model->toArray() in your PR. Do you see any issue with this change?

@cosmastech cosmastech marked this pull request as draft November 15, 2025 12:42
@cosmastech cosmastech marked this pull request as ready for review November 15, 2025 13:09
@riesjart
Copy link
Contributor

riesjart commented Nov 16, 2025

@riesjart You had mentioned there were multiple reasons for not using $model->toArray() in your PR. Do you see any issue with this change?

I think the cause of the issue can be found within the Factory::insert() method. It's using Eloquent\Builder::fillForInsert() instead of Eloquent\Builder::insert(). This also means that for each record a new model is being instantiated (again!), while there was already a collection of model instances available (see Factory::insert()). Apart from this causing the double quoting JSON, it also seems quite inefficient to me. Especially since one typically uses the insert() functionality for large record sets.

I do understand why Eloquent\Builder::fillForInsert() is being called, since the setUniqueIds() and addTimestampsToUpsertValues() calls are needed. I think it would be better to call these methods directly on the model instances already available from within the Factory::insert(). The latter addTimestampsToUpsertValues() method is currently protected though.

@cosmastech
Copy link
Contributor Author

@riesjart You had mentioned there were multiple reasons for not using $model->toArray() in your PR. Do you see any issue with this change?

I think the cause of the issue can be found within the Factory::insert() method. It's using Eloquent\Builder::fillForInsert() instead of Eloquent\Builder::insert(). This also means that for each record a new model is being instantiated (again!), while there was already a collection of model instances available (see Factory::insert()). Apart from this causing the double quoting JSON, it also seems quite inefficient to me. Especially since one typically uses the insert() functionality for large record sets.

I do understand why Eloquent\Builder::fillForInsert() is being called, since the setUniqueIds() and addTimestampsToUpsertValues() calls are needed. I think it would be better to call these methods directly on the model instances already available from within the Factory::insert(). The latter addTimestampsToUpsertValues() method is currently protected though.

That's a fair point about instantiating multiple times. Provided that prior to this change, most of us were creating these models via a series of insert statements, I still think we're on the winning end of speed.

My hope is to fix the break we have, and was curious if you see there being anything that wouldn't work, even if sub-optimal on the instantiation front.

@riesjart
Copy link
Contributor

riesjart commented Nov 16, 2025

I think trying to cast 'already-insert-ready' (raw) attributes (Model::$attributes) to their cast values back and forth to eventually insert them, is an anti-pattern and should be avoided. It is not what attributes casts and ...toArray() calls are meant for. This is why I introduced #57670.
The anti-pattern can cause all kinds of issues, I think the double quoting of the JSON values is just one that first came to light. Apart from the inefficiency at runtime, it also makes the code harder to understand and maintain.

I created #57799 as an alternative, what do you think?

@taylorotwell taylorotwell merged commit e6c54e4 into laravel:12.x Nov 17, 2025
65 of 66 checks passed
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