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

avpm's avatar
Level 1

Pluck value from nested relationship

Hello everyone

I have been searching through the documentation, as I am convinced there is an Eloquent way of doing what I am trying to do but can't seem to find it.

I have three tables that looks like that:

  • users: id, ...
  • orders: id, user_id, ...
  • order_items: id, order_id, product_id, ...

Essentially a user has many orders, and an order has many items.

My goal is to retrieve every purchased products (products id) from all the orders of a user.

I would like to avoid doing the following,

$userOrders = \App\User::with(['orders', 'orders.items'])
                                          ->findOrFail($userId);

            // This way of doing works..but it does not feel right
            $product_ids = [];
            foreach($userOrders->orders as $order) {
                foreach($order->items as $item) {
                    $product_ids []= $item->product_id;
                }
            }
            $purchased_products_ids = array_unique($product_ids);
            //var_dump($purchased_products_ids);

            // This does not work however I have the impression that the proper way of doing it
            // would be somewhat similar to this?
            // This throws an error: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'orders.items.product_id' 
        // I also tried plucking only product_id, or just items.product_id, to no avail.
            // $p = $userOrders->pluck('orders.items.product_id');

            $p = $userOrders->get()->pluck('orders.items.product_id');

Is pluck even the proper method to use to achieve what I want?

Any suggestions or guidance would be appreciated

Thank you very much

0 likes
4 replies
lostdreamer_nl's avatar

This seams to work fine:

    $user = User::with('orders.items')->findOrFail($userId);
    
    $items = collect();
    $user->orders->each(function($order) use(&$items) {
        $items = $items->concat($order->items);
    });
   dd($items->toArray());
avpm's avatar
Level 1

This does work indeed. However let's consider this code. This does exactly what I would like it to do. It returns an array with 13 and 23. Is there not a way to do it with data from the database? I haven't been able to make it work, as I mentioned in my original post.

Also, thanks a lot for your answer, I will accept it as the solution soon, I just want to confirm if there's effectively a way to do it the way I'm thinking it's possible or not.

$c = collect(
        [
            ['id' => 11,
            'order_details' =>
                ['id' => 12,
                'even_deeper_details' =>
                    ['id' => 13,
                    'details_a' => 'da'
                    ]
                ]
            ],
            ['id' => 21,
            'order_details' =>
                ['id' => 22,
                'even_deeper_details' =>
                    ['id' => 23,
                    'details_a' => 'da'
                    ]
                ]
            ],
        ]);
    var_dump($c->pluck('order_details.even_deeper_details.id')->all()); die();
   // returns [13,23]
staudenmeir's avatar

You can use a HasManyThrough relationship:

public function items() {
    return $this->hasManyThrough(OrderItem::class, Order::class);
}

$user = User::findOrFail($userId);
$product_ids = $user->items()->pluck('product_id');
4 likes

Please or to participate in this conversation.