skip to Main Content

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);
    });
}

2

Answers


  1. $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

    IlluminateSupportCollection {#4720
      #items: array:2 [
        0 => array:3 [
          "id" => 1
          "name" => "product one"
          "items" => IlluminateSupportCollection {#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" => IlluminateSupportCollection {#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
    }
    
    Login or Signup to reply.
  2. 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
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search