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 Model2{3 public function posts(): HasMany4 {5 return $this->hasMany(Post::class);6 }7}8
9class Post extends Model10{11 public function reviews(): HasMany12 {13 return $this->hasMany(Review::class);14 }15}16
17class Review extends Model18{19 public function postAuthor()20 {21 // Now what?22 }23}
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 Model2{3 public function postAuthor(): HasOneThrough4 {5 return $this->hasOneThrough(User::class, Post::class, 'id', 'id', 'post_id', 'user_id');6 }7}
The Explanation
The hasOneThrough relationship isn’t designed for inverse relationships as you’ll see when comparing our solution with the documentation:
// Reproduced from https://laravel.com/docs/10.x/eloquent-relationships#has-one-throughclass Mechanic extends Model{ /** * Get the car's owner. */ public function carOwner(): HasOneThrough { return $this->hasOneThrough( Owner::class, Car::class, '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
.