[8.x] Allow serializing custom casts when converting a model to an array #34702
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This PR allows serializing custom value objects when a model is converted to an array even if the value object does not implement the
Arrayable
interface. There are no breaking change, new functionality is covered with tests.Reasoning
Consider a custom caster that casts the value to a value object, say a BigDecimal instance. When calling
$model->toArray()
, the value is not serialized to a primitive type like a string - it remains aBigDecimal
object instance:This is inconsistent with dates, which are cast to
Carbon
instances, but serialized when converting the model to an array:A comment suggests that the value object should implement the
Arrayable
interface to serialize the value. However, there are cases where this may not be possible, feasible or would not make sense for the class itself.final
.Since
final
classes cannot be extended, the only way would be to use composition to create a custom wrapper around thefinal
class. This might work fine for simpler classes, but for something likeBigDecimal
, the maintenance overhead and code duplication would make it non-feasible (some 30+ methods), especially for smaller projects and teams.This issue was brought up in laravel/deas as well: Add JSON serialization for cast attributes that implement Jsonable or JsonSerializable interface ideas#2152
toArray()
method for a value object that represents basically a scalar value makes little sense. What would be the expected return value of(new Decimal(123.45))->toArray()
?Furthermore, dates already have special handling
serializeDate
in core (see below) - why not allow custom serialization for custom casts?framework/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php
Lines 226 to 241 in 576dd3d
What's the use case? Why do we need the values to be serialized in the array at all?
Why not simply use
Jsonable
/JsonSerializable
like suggested in laravel/ideas#2152 ?This would probably work fine for most cases - but custom serialization adds more flexibility. If dates already allow custom serialization/formats why not make it possible for custom casts as well?
Why not cast the model to JSON and the decode it back to an array?
I thought about it and while it's possible to do a little encoding dance:
... it's far from elegant, adds extra overhead (including mental) and just feels ... meh.
Conclusion
I think custom casts is a really powerful feature, especially if it's possible to ease the adoption when casting to 3rd party library classes.
This is my first PR to Laravel, so please go easy on me :) I've tried to follow the core naming and code structure conventions, however feel free to make edits or suggestions there.
If you think this contribution would be worthwhile but needs a different implementation, just let me know. I'm open to all ideas and suggestions.