Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

baspa's avatar
Level 1

Remove duplicates in relation collection and count on pivot table data

I have three different tables/models: order, lamp and the pivot table lamp_order. Order and lamp have a many-to-many relationship. In the pivot table not only the IDs are stored but also the room where the lamp should be placed in. The pivot table looks like this:

| id  | lamp_id | order_id | room     |
| ----|-------- | -------- | -------- | 
| 1   | 1       | 1         | hall    | 
| 2   | 1       | 1         | hall    |
| 3   | 2       | 1         | hall    |
| 4   | 2       | 1         | kitchen |
| 5   | 2       | 1         | kitchen |
| 6   | 3       | 1         | kitchen |

When an order needs to be changed I want to be able to change the amount of the same lamp in the room it should be placed in. So I want to group the lamps per room and lamp_id, count those and remove the duplicates. An example of how the data should look like:

'id' => 1,
'name' => 'John Doe'
'email' => '[email protected]'
'notes' => 'Lorem ipsum dolor sit amet'
'created_at' => '2020-04-02 12:32:30' 
'updated_at' => '2020-04-02 12:32:30'
'lamps_count' => 6
'grouped_lamps' => [
  [
   'id' => 1
   'name' => 'Lamp 1'
   'fitting' => 'E14'
   'shape' => 'candle'
   'room' => 'hall'
   'count' => '2'
  ],
  [
   'id' => 2
   'name' => 'Lamp 2'
   'fitting' => 'E27'
   'shape' => 'pear'
   'room' => 'hall'
   'count' => '1'
  ],
  [
   'id' => 2
   'name' => 'Lamp 2'
   'fitting' => 'E27'
   'shape' => 'pear'
   'room' => 'kitchen'
   'count' => '2'
  ],
  [
   'id' => 3
   'name' => 'Lamp 3'
   'fitting' => 'E27'
   'shape' => 'stick'
   'room' => 'kitchen'
   'count' => '1'
  ],
];

I currently have this function in my controller, it isn't finished yet because it doesn't return the preferred data structure:

   $orders = Order::withCount('lamps')
            ->with('lamps.image')
            ->get()
            ->map(static function (Order $order) {
                $order->lamps->each(static function (Lamp $lamp) {
                    $lamp->room = $lamp['pivot']['room'];
                });

                return $order;
            })->each(function (Order $order) {
                $groupedLamps = $order->lamps->groupBy('room');
                $order->lamps->each(function (Lamp $lamp) use ($groupedLamps) {
                    $lamp->count = $groupedLamps[$lamp->room]->count();
                });
            });

        return response()->json([
            'orders' => $orders,
        ], 200);

I have a function in my order model to get the related lamps like this:

    public function lamps()
    {
        return $this->belongsToMany(Lamp::class, 'lamp_order', 'order_id', 'lamp_id')
            ->withPivot('room');
    }

The data it returns is almost right but the count of the lamps is not right because it counts the lamps per room what results in a data structure like this:

'id' => 1,
'name' => 'John Doe'
'email' => '[email protected]'
'notes' => 'Lorem ipsum dolor sit amet'
'created_at' => '2020-04-02 12:32:30' 
'updated_at' => '2020-04-02 12:32:30'
'lamps_count' => 6
'grouped_lamps' => [
  [
   'id' => 1
   'name' => 'Lamp 1'
   'fitting' => 'E14'
   'shape' => 'candle'
   'room' => 'hall'
   'count' => '3' // total amount of lamps for room hall
  ],
  [
   'id' => 1
   'name' => 'Lamp 1'
   'fitting' => 'E14'
   'shape' => 'candle'
   'room' => 'hall'
   'count' => '3' // total amount of lamps for room hall
  ],
  [
   'id' => 2
   'name' => 'Lamp 2'
   'fitting' => 'E27'
   'shape' => 'pear'
   'room' => 'hall'
   'count' => '3' // total amount of lamps for room hall
  ],
  [
   'id' => 2
   'name' => 'Lamp 2'
   'fitting' => 'E27'
   'shape' => 'pear'
   'room' => 'kitchen'
   'count' => '3' // total amount of lamps for room kitchen
  ],
  [
   'id' => 2
   'name' => 'Lamp 2'
   'fitting' => 'E27'
   'shape' => 'pear'
   'room' => 'kitchen'
   'count' => '3' // total amount of lamps for room kitchen
  ],
  [
   'id' => 3
   'name' => 'Lamp 3'
   'fitting' => 'E27'
   'shape' => 'stick'
   'room' => 'kitchen'
   'count' => '3' // total amount of lamps for room kitchen
  ],
];

I also want to remove the duplicates from the array like in the preferred data example I gave. Any help is appreciated, if you need anymore information or data then I am more than willing to help you. Thanks in advance.

0 likes
2 replies
bugsysha's avatar

Hard one to understand. Can you simplify or ask the question again?

localpathcomp's avatar

You can query by the thing that would make the row unique then delete using those unique values with a limit 1. Let this run and it will guarantee in a safe way you'll only be left with a pivot table of unique rows based on what you consider to the be the unique values, ie generally the two many-to-many related models.

As an example:

$duplicates = DB::table("organization_user")
  ->select(["organization_id", "user_id", DB::raw("COUNT(*) as `count`")])
  ->groupBy("organization_id", "user_id")
  ->havingRaw("COUNT(*) > 1")
  ->get()
  ->each(
    fn($dupe) => DB::table("organization_user")
      ->where("organization_id", $dupe->organization_id)
      ->where("user_id", $dupe->user_id)
      ->limit(1)
      ->delete()
  );

Please or to participate in this conversation.