I know this has been asked several times, but none of the answers work for me.
I have a collection of products; each has an eloquent relationship containing multiple items.
Here's how I'm retrieving the collection
$collections = Collection::with('items')->get();
And this is the result of return $collections
from the controller.
[
{
"id": 1,
"name": "Product One",
"items": [
{
"id": 1,
"product_id": 1,
"name": "Item One",
},
{
"id": 2,
"product_id": 1,
"name": "Item Two",
},
{
"id": 3,
"product_id": 1,
"name": "Item Three",
},
},
{
"id": 2,
"name": "Product Two",
"items": [
{
"id": 1,
"product_id": 2,
"name": "Item One",
},
{
"id": 2,
"product_id": 2,
"name": "Item Two",
},
{
"id": 3,
"product_id": 2,
"name": "Item Three",
},
}
]
I'd like to sort each product's items in a different order.
I'd like to sort Product One's items as 3, 1, 2
and Product Two's items as 2, 3, 1
.
So I created an array of the new sort order, and then added a callback
$newSortOrder = [
1 => [3, 1, 2],
2 => [2, 3, 1]
];
$collections = Collection::with('items')->get();
foreach ($collections as $collection) {
$collection->items->sortBy(function ($model) use ($newSortOrder) {
return array_search($model->id, $newSortOrder[$model->id]);
});
}
dd($collections); <-- this is still in the default sort order from the db
However, when I return $collections, the items are still in the default order. What am I doing wrong?
Edit: tried this as well but with same results; $collection items are returned in the default order.
foreach ($collections as $collection) {
$collection->items->sortBy(function ($model) use ($order) {
return array_search($model->getkey(), $order);
});
}
CodePudding user response:
$sortOrder = [
1 => [3, 1, 2],
2 => [2, 3, 1]
];
$sorted = $products->map(function($product) use ($sortOrder) {
$order = $sortOrder[$product->id];
return [
...$product,
'items' => $product->items->mapWithKeys(
fn($item) => [array_search($item['id'], $order) => $item]
)->sortKeys()
];
});
This Outputs
Illuminate\Support\Collection {#4720
#items: array:2 [
0 => array:3 [
"id" => 1
"name" => "product one"
"items" => Illuminate\Support\Collection {#4722
#items: array:3 [
0 => array:2 [
"id" => 3
"name" => "three"
]
1 => array:2 [
"id" => 1
"name" => "one"
]
2 => array:2 [
"id" => 2
"name" => "two"
]
]
#escapeWhenCastingToString: false
}
]
1 => array:3 [
"id" => 2
"name" => "product two"
"items" => Illuminate\Support\Collection {#4721
#items: array:3 [
0 => array:2 [
"id" => 2
"name" => "two"
]
1 => array:2 [
"id" => 3
"name" => "three"
]
2 => array:2 [
"id" => 1
"name" => "one"
]
]
#escapeWhenCastingToString: false
}
]
]
#escapeWhenCastingToString: false
}
CodePudding user response:
you can recreate items using map()
.
// not recommend because key does not support for correct item's id
// $newSortOrder = [
// 1 => [3, 1, 2],
// 2 => [2, 3, 1]
// ];
$newSortOrder = [
[
"id" => 1,
"sortOrder" => [3, 1, 2]
],
[
"id" => 2,
"sortOrder" => [2, 3, 1]
],
];
$collections = Collection::with('items')->get();
$sortedCollection = collect($collections)->map(function($item) use($newSortOrder) {
$id = data_get($item,'id');
$name = data_get($item,'name');
$originItems = data_get($item,'items');
$sortOrderList = data_get(collect($newSortOrder)->firstWhere('id',$id),'sortOrder');
$items = collect($sortOrderList)->map(function($sortNumber) use($originItems) {
return collect($originItems)->firstWhere('id',$sortNumber);
});
return compact('id','name','items');
});
dd($sortedCollection); // result