php - id attribute returns value 0 inside a map function in laravel
Code
$drillDownChart = Loan::select('loan_type AS name')
->groupBy('loan_type')
->where('transaction_year', $transyear)
->where('invalid', false)
->get();
$drillDownChart->map(function($val)use($loanChartArr) {
$val->id = $val->name;
$val->data = $loanChartArr;
return $val;
});
Output var_dump(json_encode($drillDownChart));
string(340) "[{"name":"Hospitalization","id":0,"data":[[1,0],[2,0],[3,0],[4,0],[5,2],[6,1],[7,3],[8,1],[9,1],[10,0],[11,0],[12,0]]},{"name":"Salary","id":0,"data":[[1,0],[2,0],[3,0],[4,0],[5,2],[6,1],[7,3],[8,1],[9,1],[10,0],[11,0],[12,0]]},{"name":"Emergency","id":0,"data":[[1,0],[2,0],[3,0],[4,0],[5,2],[6,1],[7,3],[8,1],[9,1],[10,0],[11,0],[12,0]]}]"
The $val->id = $val->name;
should return "Hospitalization", "Salary" and "Emergency" but instead it returns 0. Notice if I change "id" to "ID", it shows the correct output.
$drillDownChart->map(function($val)use($loanChartArr) {
$val->ID = $val->name;
$val->data = $loanChartArr;
return $val;
});
Output:
string(373) "[{"name":"Hospitalization","ID":"Hospitalization","data":[[1,0],[2,0],[3,0],[4,0],[5,2],[6,1],[7,3],[8,1],[9,1],[10,0],[11,0],[12,0]]},{"name":"Salary","ID":"Salary","data":[[1,0],[2,0],[3,0],[4,0],[5,2],[6,1],[7,3],[8,1],[9,1],[10,0],[11,0],[12,0]]},{"name":"Emergency","ID":"Emergency","data":[[1,0],[2,0],[3,0],[4,0],[5,2],[6,1],[7,3],[8,1],[9,1],[10,0],[11,0],[12,0]]}]"
What is wrong in here? Is "id" restricted to be used in laravel in this case? Please help. Thank you.
Answer
Solution:
By default, Laravel assumes your model has a primary key named id
with a value of type int
.
Therefore, the id
attribute is casted to an int
. It is like having this default casts:
protected $casts = [
'id' => 'int'
];
So when you access id
, your string
(e.g. "Hospitalization") is casted to int
. The result is 0
.
If you really want to keep your current code, you'll have to do some hacky stuff, like setting a accessor for the id
attribute:
public function getIdAttribute($value)
{
return $value;
}
This way, the cast will be overriden and you will be able to retrieve the name the way you want.
However, I would advice against manually changing the id
at runtime, it is obviously a code smell, a sign that something isn't quite right with this part of your project.
For those who like digging further, this is where all what I explained happens:
vendor/laravel/framework/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php
/**
* Get the casts array.
*
* @return array
*/
public function getCasts()
{
if ($this->getIncrementing()) {
return array_merge([$this->getKeyName() => $this->getKeyType()], $this->casts);
}
return $this->casts;
}
When you try to access an attribute ($this->id
), Laravel will determine if it has an accessor, if this is a relationship or not... and at the end, will cast it to the correct type.
To get the casts, it will call ->getCasts()
.
First this method will check if your model is auto incrementing (which can be disabled by setting the property $incrementing
to false
).
If that's the case, it will get the primary key name (->getKeyName()
which is id
by default) and its type (->getKeyType()
which is int
by default).
This key pair will be merged with the $casts
property of the model.