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

secondman's avatar

hasManyThrough not returning a relation

I'm trying to use the hasManyThrough relationship but it doesn't fetch the related model.

3 tables:

driver_routes
id
--- other fields
driver_route_details
id
route_id (FK to driver_routes.id)
callout_id (FK to callouts.id)
--- other fields
callouts
id
---other fields

The relationships on my models that are related:

DriverRoute.php:

public function details() 
{
    return $this->hasMany(DriverRouteDetail::class, 'route_id', 'id');
}

public function callouts()
{
    return $this->hasManyThrough(Callout::class, DriverRouteDetail::class, 'callout_id', 'id', 'id');
}

DriverRouteDetail.php

public function callout() 
{
    return $this->belongsTo(Callout::class, 'callout_id', 'id');
}

public function route() 
{
    return $this->belongsTo(DriverRoute::class, 'route_id', 'id');
}

Callout.php

public function details()
{
    return $this->hasMany(DriverRouteDetail::class, 'callout_id', 'id');
}

Looking at the Docs this seems correct, but when I request my driver routes, no callouts are related when in fact it should return at least one.

$routes = DriverRoute::with('callouts', 'details')->get();

This returns my DriverRouteDetail models correctly, but the callouts relation is empty.

What am I doing wrong here?

0 likes
8 replies
usama.ashraf's avatar

That's not a use-case for hasManyThrough. Leave that relationship and just do a nested eager load:

$routes = DriverRoute::with('details.callout')->get();
secondman's avatar

@usama.ashraf

Thanks, I tried that already, it returns the following error:

SQLSTATE[42S22]: Column not found: 1054 Unknown column 'callouts.route_id' in 'on clause' (SQL: select `callouts`.*, `driver_route_details`.`callout_id` from `callouts` inner join `driver_route_details` on `driver_route_details`.`id` = `callouts`.`route_id` where `driver_route_details`.`callout_id` in (1))

Appreciate your help.

-V

usama.ashraf's avatar

I updated my answer after a second thought.

That's not a use-case for hasManyThrough. Leave that relationship and just do a nested eager load:

$routes = DriverRoute::with('details.callout')->get();
secondman's avatar

@usama.ashraf

I thought of that as well, but I need a collection of just the callouts that belong to the route as well as being able to reference them within the view of the details.

So you're saying I should just loop through and collect them myself in the code? I don't see why this isn't a value use case for hasManyThrough.

Thanks again.

usama.ashraf's avatar

I'm also assuming that hasManyThrough isn't strictly one-way so may be you could try this (I never have though):

return $this->hasManyThrough(Callout::class, DriverRouteDetail::class, 'route_id', 'id', 'callout_id');

Sorry for the mess.

minjon's avatar

Checkout your foreign keys, there is an error in joining DriverRouteDetails and Callouts. Error msg says that your DriverRouteDetails id column points out to Callouts route_id column and route_id column does not exist in your callouts table.

As far as I understand, DriverRouteDetails callout_id should point out to Callouts id.

I would suggest that you try with omitting all parameters except Class names in your relationship methods, because they are not mandatory if your parents models (callouts, routes) use id as primary key and you want to join your child model (DriversRouteDetails) with those id columns.

secondman's avatar

@minjon

Thanks. I've had to abandon this is it seems there's no key in the callouts table to join on. No matter what combination of keys I use the relationship cannot be established. Weird.

Thanks.

Please or to participate in this conversation.