Search code examples
laravelcollectionsresponsecodable

Data output via the Laravel API for a grouped collection


My problem is that I can't bring the output of a list of products grouped by category, for further deserialization in an iOS application using the Decodable protocol.

In my Laravel 10 application there are 3 entities: carts, products and categories, relationships are set up between them, that a category can contain many products, and a product has one category, this part works.

When I get the necessary products that are in the basket:

$collection = DB::table('cart_product')
    ->where('cart_id', $validRequest['cart_id'])
    ->join('products', 'cart_product.product_id', '=', 'products.id')
    ->join('categories', 'products.category_id', '=', 'categories.id')
    ->select('categories.name as category', 'cart_product.id', 'products.name as product', 'cart_product.count')
    ->get();

return $collection;

I get the following output:

[
  {
    "category": "Plumbing",
    "id": 2,
    "product": "Sink",
    "count": "1.00"
  },
  {
    "category": "Dairy products",
    "id": 1,
    "product": "Milk",
    "count": "2.00",
  },
  {
    "category": "Dairy products",
    "id": 5,
    "product": "Yogurt",
    "count": "4.00",
  }
]

After applying the grouping method to the groupBy collection return $collection->groupBy('category');, I get a grouped collection:

{
  "Plumbing": [
    {
      "category": "Plumbing",
      "id": 2,
      "product": "Sink",
      "count": "1.00"
    }
  ],
  "Dairy products": [
    {
      "category": "Dairy products",
      "id": 1,
      "product": "Milk",
      "count": "2.00",
    },
    {
      "category": "Dairy products",
      "id": 5,
      "product": "Yogurt",
      "count": "4.00",
    }
  ]
}

But in response to the request, I need to provide data with static keys, which in the future should be correctly deserialized in the consumer application, that is, correspond to the following example:

[
  {
    "category": "Plumbing",
    "products": [
      {
        "id": 2,
        "product": "Sink",
        "count": "1.00"
      }
    ]
  },
  {
    "category": "Dairy products",
    "products": [
      {
        "id": 1,
        "product": "Milk",
        "count": "2.00",
      },
      {
        "id": 5,
        "product": "Yogurt",
        "count": "4.00",
      }
    ]
  }
]

I tried various examples from the official documentation on working with collections (group By, group By() + map(), mapWithKeys(), mapToGroups()), tried to perform this processing in the resource and collections handler, but I can't get the data output in the right format, Google also does not give relevant answers to my problem.

I ask for help in solving the problem.


Solution

  • Try this may help you

    $collection = DB::table('cart_product')
        ->where('cart_id', $validRequest['cart_id'])
        ->join('products', 'cart_product.product_id', '=', 'products.id')
        ->join('categories', 'products.category_id', '=', 'categories.id')
        ->select('categories.name as category', 'cart_product.id', 'products.name as product', 'cart_product.count')
        ->get();
    
    $collection = $collection->groupBy('category')->map(function ($item, $key) {
        return [
            'category' => $key,
            'products' => $item->map(function ($product) {
                return [
                    'id' => $product->id,
                    'product' => $product->product,
                    'count' => $product->count
                ];
            })->values()
        ];
    })->values();