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

[5.3] Custom casting of eloquent properties #16305

Closed
wants to merge 1 commit into from
Closed

[5.3] Custom casting of eloquent properties #16305

wants to merge 1 commit into from

Conversation

browner12
Copy link
Contributor

currently we gets dates autocast to Carbon objects, and we can cast any property to primitive types.

this PR allows us to cast properties to our own custom-defined types (usually objects i'm guessing).

Here is my use case. I have an Order model that has subtotal, tax, shipping, and total fields. I want them all to be instances of the new Money\Money class, so I can work with monetary values easier. To do this I would simply set these fields in my cast property:

protected $casts = [
    'subtotal' => 'money',
    'tax'          => 'money',
    'shipping' => 'money',
    'total'        => 'money,
];

Then I just need to define a castToMoney() method on the class:

public function castToMoney($value)
{
    return new \Money\Money($value, new \Money\Currency('USD'));
}

Now when accessing the property on the model, it would automatically give me back the Money object:

$order = \App\Order::first();

$order->total;  //returns Money object

$order->total->getAmount(); //returns the value e.g. 127

I think the easiest way to define these 'cast methods' would be as a trait that was imported to models where it was needed. If your models extended a base class, it could also be defined there.

I wasn't able to a good test for how this behaves when the model is turned into an array ($order->toArray()). wondering what thoughts would be for this.

I can foresee someone saying why not just define the property with an 'Accessor'. My argument would be that's fine for 1-off field manipulation, but when you are doing the same casting over and over this an easier more consistent way. If you ever need to swap out one of these 'cast methods' you can do it in one place.

Thanks for any feedback.

allows us to cast to non-primitives.
@GrahamCampbell GrahamCampbell changed the title custom casting of eloquent properties [5.3] Custom casting of eloquent properties Nov 7, 2016
@JosephSilber
Copy link
Member

Is your Money class immutable, or can its value change?

What happens when you change it, and then save your model?

With your current implementation, the record in the database will not be updated.

@CristianLlanos
Copy link

CristianLlanos commented Nov 8, 2016

I believe we should consider get and set casters. I made something related to this in my package's model class. However, I think @JosephSilber does have a point.

@taylorotwell
Copy link
Member

Is it worth revisiting my implementation of this? #13706

@browner12
Copy link
Contributor Author

I think so. How would #13706 work with packages? would we have to subclass to add the 2 methods?

the specific package I sent this PR for was because of https://github.com/moneyphp/money

unfortunately they make their Money class final so I'm not sure how we could make this integrate.

@taylorotwell
Copy link
Member

Blah, final classes are so supersitious. :(

I’ll have to think on that.

@alexbowers
Copy link
Contributor

Custom casts would mean less / no changes are necessary in core for laravel/ideas#293

switch ($this->getCastType($key)) {
$castType = $this->getCastType($key);

if (method_exists($this, 'castTo' . strtoupper($castType))) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be worth to tweak that a bit:

if (method_exists($this, $method = 'castTo'.ucfirst(Str::camel($castType)))) {
    return $this->$method($value);
}

So that 'foo_bar_baz' type could be handled by castToFooBarBaz($value) ?

@browner12
Copy link
Contributor Author

i feel like maybe we should close my PR. I feel like this may be a bigger issue that needs to be thought out, with a good plan of action. just trying to avoid having the PR sit in limbo.

thoughts?

@faustbrian
Copy link
Contributor

@browner12 #16460 was another attempt on this with more control but also got closed.

@taylorotwell
Copy link
Member

Yeah, I think this particular implementation is not going to be preferred because the modifications of the casted models do not get synced back into the model before save. My PR does not that but as noted will not work with "final" classes since they are artificially restrict developer freedom unfortunately. ☹️

@browner12
Copy link
Contributor Author

that's fine by me. thanks for closing this. hopefully we can find some solution for this.

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.

None yet

7 participants