A HasManyThrough relationship allows a model to access related records through an intermediate model, without directly linking the two tables.

Real-World Examples

  • Country → Users → Posts
  • Course → Modules → Lessons
  • Company → Departments → Employees
  • Category → Products → Reviews

1.When to Use HasManyThrough

Use hasManyThrough when:

  • You have two one-to-many relationships
  • You want to skip the middle model while querying
  • You want clean & optimized queries

❌ Do NOT use it for many-to-many relationships.

2. Example Scenario (Country → Users → Posts)

Relationship Chain

Country
↓
Users
↓
Posts

Goal:
👉 Get all posts of a country, without manually looping users.

3.Database Structure

countries table

id
name

users table

id
country_id
name

posts table

id
user_id
title
content

4.Model Creation

php artisan make:model Country
php artisan make:model User
php artisan make:model Post

5.Define Normal Relationships First

Country.php

public function users()
{
return $this->hasMany(User::class);
}

User.php

public function posts()
{
return $this->hasMany(Post::class);
}

6.Define HasManyThrough Relationship

Country.php

public function posts()
{
return $this->hasManyThrough(
Post::class,   // Final model
User::class    // Intermediate model
);
}

✔ That’s it! Laravel figures out the keys automatically.

7.How Laravel Resolves the Keys

Laravel assumes:

Key Type Column
Foreign key on users country_id
Foreign key on posts user_id
Local key on countries id
Local key on users id

8.Fetch Data Using HasManyThrough

$country = Country::find(1);
$posts = $country->posts;

Loop Example

foreach ($country->posts as $post) {
echo $post->title;
}

9.SQL Behind the Scene (Simplified)

SELECT posts.*
FROM posts
JOIN users ON users.id = posts.user_id
WHERE users.country_id = 1;

✔ Single optimized query

10.Custom Foreign & Local Keys

If your column names are different:

public function posts()
{
return $this->hasManyThrough(
Post::class,      // Final model
User::class,      // Intermediate model
'country_ref',    // FK on users table
'author_ref',     // FK on posts table
'id',             // Local key on countries
'id'              // Local key on users
);
}

Method Parameters Order

hasManyThrough(
FinalModel,
IntermediateModel,
foreignKeyOnIntermediate,
foreignKeyOnFinal,
localKey,
localKeyOnIntermediate
)

11.Add Conditions

$country->posts()->where('status', 'published')->get();

12.Ordering Results

$country->posts()->latest()->get();

13.Count Related Records

$country->posts()->count();

Optimized:

Country::withCount('posts')->get();

14.Eager Loading HasManyThrough

$countries = Country::with('posts')->get();

15.Real LMS Example (Course → Modules → Lessons)

Database

courses → modules → lessons

Course.php

public function lessons()
{
return $this->hasManyThrough(
Lesson::class,
Module::class
);
}

Usage

$course = Course::find(1);
$course->lessons;

✔ No need to loop modules manually

Categorized in:

Laravel,