Robert Tillman
Robert Tillman

Reputation: 1013

Merge objects of an array in PHP using Eloquent

I have a collection of objects grabbed out of my DB using Laravel's Eloquent ORM ( I am not using Laravel just integrated Eloquent into my own Framework ). I used the transform method to iterate over the collection and unserialize one of the columns of each record, then collapsed the collection to place all the unserialized objects in one array.

Here is the logic:

  $orders = Order::where('user', $user)->orderBy('id', 'desc')->get();

  $orders->transform(function($order, $key) {

    $order->cart = unserialize($order->cart);

    $items = $order->cart->items;

    return $items;
  });

  $collapsed = $orders->collapse();

And Output:

[
   {
      "qty": "2",
      "price": 200,
      "item": {
         "id": 1,
         "title": "Black Hoodie",
         "img": "https://s3.amazonaws.com/bucket/black-hoodie.jpg",
         "description": "Plain Black Hoodie.",
         "price": 100
      }
   },
   {
      "qty": "2",
      "price": 200,
      "item": {
         "id": 2,
         "title": "Green Hoodie",
         "img": "https://s3.amazonaws.com/bucket/green-hoodie.jpg",
         "description": "Plain Green Hoodie.",
         "price": 100
      }
   },
   {
      "qty": 1,
      "price": 100,
      "item": {
         "id": 2,
         "title": "Green Hoodie",
         "img": "https://s3.amazonaws.com/bucket/green-hoodie.jpg",
         "description": "Plain Green Hoodie.",
         "price": 100
      }
   },
   {
      "qty": 1,
      "price": 100,
      "item": {
         "id": 1,
         "title": "Black Hoodie",
         "img": "https://s3.amazonaws.com/bucket/black-hoodie.jpg",
         "description": "Plain Black Hoodie.",
         "price": 100
      }
   }
]

Now what I want to accomplish next is to combine all the identical objects in this array, ideally by their "item":{"id"} value, into one object - adding their qty and price properties together, leaving the item property the same.

My Desired Output Would Be

[
   {
      "qty": "3",
      "price": 300,
      "item": {
         "id": 1,
         "title": "Black Hoodie",
         "img": "https://s3.amazonaws.com/bucket/black-hoodie.jpg",
         "description": "Plain Black Hoodie.",
         "price": 100
      }
   },
   {
      "qty": "3",
      "price": 300,
      "item": {
         "id": 2,
         "title": "Green Hoodie",
         "img": "https://s3.amazonaws.com/bucket/green-hoodie.jpg",
         "description": "Plain Green Hoodie.",
         "price": 100
      }
   }
]

Eloquent has TONS of great methods available to work with collections, I'm just stuck on the right combination to efficiently achieve what I want to do.

Upvotes: 4

Views: 429

Answers (1)

krisanalfa
krisanalfa

Reputation: 6438

Try this:

use Illuminate\Support\Fluent;
use Illuminate\Support\Collection;

// Mocking data to meet your requirements.
// Wrapping attributes inside fluent object,
// so we'll have almost same object like Eloquent model
$orders = new Collection([
    new Fluent([
        'qty' => 2,
        'price' => 200,
        'item' => new Fluent([
            'id' => 1,
            'price' => 100,
            'title' => 'Black Hoodie',
            'img' => 'https://s3.amazonaws.com/bucket/black-hoodie.jpg',
            'description' => 'Plain Black Hoodie.',
        ])
    ]),
    new Fluent([
        'qty' => 2,
        'price' => 200,
        'item' => new Fluent([
            'id' => 2,
            'price' => 100,
            'title' => 'Green Hoodie',
            'img' => 'https://s3.amazonaws.com/bucket/green-hoodie.jpg',
            'description' => 'Plain Green Hoodie.',
        ])
    ]),
    new Fluent([
        'qty' => 1,
        'price' => 100,
        'item' => new Fluent([
            'id' => 2,
            'price' => 100,
            'title' => 'Green Hoodie',
            'img' => 'https://s3.amazonaws.com/bucket/green-hoodie.jpg',
            'description' => 'Plain Green Hoodie.',
        ])
    ]),
    new Fluent([
        'qty' => 1,
        'price' => 100,
        'item' => new Fluent([
            'id' => 1,
            'price' => 100,
            'title' => 'Black Hoodie',
            'img' => 'https://s3.amazonaws.com/bucket/black-hoodie.jpg',
            'description' => 'Plain Black Hoodie.',
        ])
    ])
]);

// Here's something you might interested
$result = $orders->groupBy('item.id')
    ->map(function ($orders, $itemId) {
        return new Fluent([
            'qty' => $orders->sum('qty'), // calculating quantity
            'price' => $orders->sum('price'), // calculating total price
            'item' => $orders->first()->item // leave the item as it is
        ]);
    });

// You may evaluate the result here
dd($result);

Update

More "raw" example:

use Illuminate\Support\Collection;

$orderOne = [
    'id' => 1,
    'price' => 100,
    'title' => 'Black Hoodie',
    'img' => 'https://s3.amazonaws.com/bucket/black-hoodie.jpg',
    'description' => 'Plain Black Hoodie.',
];

$orderTwo = [
    'id' => 2,
    'price' => 100,
    'title' => 'Green Hoodie',
    'img' => 'https://s3.amazonaws.com/bucket/green-hoodie.jpg',
    'description' => 'Plain Green Hoodie.',
];

$orders = new Collection([
    [
        'qty' => 2,
        'price' => 200,
        'item' => $orderOne
    ],
    [
        'qty' => 2,
        'price' => 200,
        'item' => $orderTwo
    ],
    [
        'qty' => 1,
        'price' => 100,
        'item' => $orderTwo
    ],
    [
        'qty' => 1,
        'price' => 100,
        'item' => $orderOne
    ]
]);

$result = $orders->groupBy('item.id')
    ->map(function ($orders, $itemId) {
        return [
            'qty' => $orders->sum('qty'),
            'price' => $orders->sum('price'),
            'item' => $orders->first()['item']
        ];
    });

dd($result);

Upvotes: 1

Related Questions