I have to retrieve just an array of id from the given collection, something like [10,54,61,21,etc]. I've tried flatten, pluck, but nothing seems to work apart from a foreach which is something I would like to remove at this step.
// Model
class Children extends Eloquent {
public function directChildrens(){
return $this->hasMany('App\Children','father_id','id')->select('id','father_id');
}
public function childrens(){
return $this->directChildrens()->with('childrens');
}
}
// Controller
return $children->childrens()->get();
As expected it works fine. Here a result:
[{
"id": 10,
"father_id": 2,
"childrens": [
{
"id": 54,
"father_id": 10,
"childrens": [
{
"id": 61,
"father_id": 54,
"childrens": []
}
]
},
{
"id": 21,
"father_id": 10,
"childrens": []
}
]
}]
How can I perform a pluck('id') of this collection in order to get [10,54,61,21] ?
$result = [
[
"id" => 10,
"father_id" => 2,
"childrens" => [
[
"id" => 54,
"father_id" => 10,
"childrens" => [
[
"id" => 61,
"father_id" => 54,
"childrens" => []
]
]
],
[
"id" => 21,
"father_id" => 10,
"childrens" => []
]
]
]
];
return collect($result)
->map(function ($value) {
return Arr::dot($value); // converts it to the dot notation version (added the example at the end)
})
->collapse() // collapse them into a single one
->filter(function ($value, $key) {
return $key === 'id' || Str::endsWith($key, '.id'); // filter first id + patterns of .id
})
->values() // discard dot notation keys
->toArray();
Arr::dot()
method turned collection into the following format which flattens all into the same level.
[
{
"id": 10,
"father_id": 2,
"childrens.0.id": 54,
"childrens.0.father_id": 10,
"childrens.0.childrens.0.id": 61,
"childrens.0.childrens.0.father_id": 54,
"childrens.0.childrens.0.childrens": [],
"childrens.1.id": 21,
"childrens.1.father_id": 10,
"childrens.1.childrens": []
}
]
Thanks to @Dan warning about deprecated/removed function array_dot
. Replaced with Arr::dot()
You can call array_walk_recursive
only once, I just wraped into a pipe
method to use functional approach used in Eloquent and Collections
return $children->childrens()->get()->pipe(function ($collection) {
$array = $collection->toArray();
$ids = [];
array_walk_recursive($array, function ($value, $key) use (&$ids) {
if ($key === 'id') {
$ids[] = $value;
};
});
return $ids;
});
Note that you must convert collection to array and store it in a variable as array_walk_recursive
first parameter is passed by reference, then the following code would cause fatal error Only variables should be passed by reference
array_walk_recursive($collection->toArray(), function (){
// ...
})
A recursive solution right here, requires to declare an additional method though:
function extractIds($obj)
{
$result = [];
if (isset($obj['id'])) {
$result[] = $obj['id'];
}
if (isset($obj['children'])) {
return array_merge($result, ...array_map('extractIds', $obj['children']));
}
return $result;
}
The method will securely check for the id
property as well as the childrens
property before recursing into it. (By the way, the word children
is already the plural form, no need for an additional s
at the end. So be aware I removed it in my example.)
Using this, we can utilize a Laravel collection to easily chain the required transformation calls:
$result = collect($array)
->map('extractIds')
->flatten()
->toArray();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With