A through relationship crosses an intermediate table in one JOIN. Category::reviews() is hasManyThrough(Review::class, Product::class) with every key guessed, and $this->through( 'products')->has('reviews') builds the same from existing relationships. Six arguments cover the rest, such as an order's address, reached through the customer the order belongs to:
public function shippingAddress(): HasOneThrough
{
return $this->hasOneThrough(Address::class, Customer::class,
'id', // customers column that orders.customer_id matches
'customer_id', // addresses column that points at customers
'customer_id', // orders column holding the customer
'id'); // customers column that addresses point at
}use App\Models\{Category, Order};
echo Category::find(3)->reviews->pluck('rating')->join(', '), "\n";
echo Order::find(4)->shippingAddress->city, "\n";Output
-- select * from `categories` where `categories`.`id` = 3 limit 1
-- select `reviews`.*, `products`.`category_id` as `laravel_through_key` from `reviews` inner
join `products` on `products`.`id` = `reviews`.`product_id` where `products`.`category_id`
= 3
4, 3
...
Sao Paulolaravel_through_key carries the parent key back through the join so eager loading can match rows. For chains longer than one hop, eloquent-has-many-deep 2,873 (https://github.com/staudenmeir/eloquent-has-many-deep 2,873 ) (1.22 for Laravel 13 2,157 ) extends the idea to any depth.