Inverse BelongsToThrough relationships in Laravel models

No, Laravel doesn’t actually have a BelongsToThrough relationship, a common frustration for models where you want to access the top level of a BelongsTo chain.

Consider this setup:

1class User extends Model
3 public function posts(): HasMany
4 {
5 return $this->hasMany(Post::class);
6 }
9class Post extends Model
11 public function reviews(): HasMany
12 {
13 return $this->hasMany(Review::class);
14 }
17class Review extends Model
19 public function postAuthor()
20 {
21 // Now what?
22 }

A user has authored many posts. Those posts have many reviews. So how do we define the inverse relationship back up from Review -> User (the post author) without having to load Post in the middle?

The Solution

Enter the hasOneThrough relationship, albeit slightly repurposed.

1class Review extends Model
3 public function postAuthor(): HasOneThrough
4 {
5 return $this->hasOneThrough(User::class, Post::class, 'id', 'id', 'post_id', 'user_id');
6 }

The Explanation

The hasOneThrough relationship isn’t designed for inverse relationships as you’ll see when comparing our solution with the documentation:

// Reproduced from
class Mechanic extends Model
* Get the car's owner.
public function carOwner(): HasOneThrough
return $this->hasOneThrough(
'mechanic_id', // Foreign key on the cars table...
'car_id', // Foreign key on the owners table...
'id', // Local key on the mechanics table...
'id' // Local key on the cars table...

Because we want to invert the relationship, we have to invert the foreign keys for local keys and the local keys for foreign keys. So ours works out at:

class Review extends Model
public function postAuthor(): HasOneThrough
return $this->hasOneThrough(
User::class, // target class
Post::class, // through class
'id', // local key on the posts table
'id', // local key on the users table
'post_id', // foreign key on the reviews table
'user_id' // foreign key on the posts table

So now, if you have Review model, you can skip the Post model entirely with $review->postAuthor.