Reputation: 11636
I have a decimal field to represent money in my Eloquent Model and I'm noticing erroneous results when trying to add some number to this field.
Upon further inspection, I found that the decimal field is being cast as a string,
like "1245114.00"
in the tinker console.
I checked the table structure and I can verify that the field is indeed decimal(11,3)
.
This question was asked before but has no answers.
Why is this happening?
Upvotes: 33
Views: 43806
Reputation: 3214
Also notice that 0.000 will give you true.
To get numeric value of it or to compare:
Or
$value = +$model->number_field_decimal
Or
$value = 1*$model->number_field_decimal
Upvotes: 0
Reputation: 1060
That's the way it should be since PHP has no decimal type. If you cast decimal to float you may loose precision.
The best option is leaving it as it is, as strings. Then use bcmath to operate, which is meant to be used with strings.
Upvotes: 7
Reputation: 17132
I just spent some time analyzing this issue because my Laravel model shows database decimal columns as strings.
The important thing is that adding typecasting to float in the model does fix it, but if you were like me, you noticed dd(\App\SomeModel::someScope()->get());
still shows those decimal columns as strings after adding typecasting in the model.
When these values are sent to the client, they are integers, so the typecasting did fix the original problem, thus allowing my JavaScript to receive Number
not String
.
I am just making this answer so a person doesn't waste time due to focusing on dd()
output. I investigated solutions about the PDO driver, and while some perhaps have merit, it isn't a big deal because the real database outputs are cast to the correct type.
Upvotes: 5
Reputation: 676
According to this thread on Laravel's github repository: https://github.com/laravel/framework/issues/11780
It seems to be a PDO driver issue. Automatically casting decimals to strings. People in that thread said that it was a problem with the mysql 5.2 driver and some rolled back to 5.1. If you're on your own server it you'll be able to downgrade. Otherwise you'll have to cast it to float on model level.
Upvotes: 10
Reputation: 7923
You need to define in your model which fields need to be cast to a primitive attribute.
protected $casts = [
'my_decimal' => 'float',
];
The
$casts
property on your model provides a convenient method of converting attributes to common data types. The$casts
property should be an array where the key is the name of the attribute being cast and the value is the type you wish to cast the column to. The supported cast types are:integer
,real
,float
,double
,string
,boolean
,object
,array
,collection
,date
,datetime
, andtimestamp
There is a really good explanation here:
https://mattstauffer.com/blog/laravel-5.0-eloquent-attribute-casting/
Also there is an explanation in the docs:
https://laravel.com/docs/5.5/eloquent-mutators#attribute-casting
Upvotes: 56