How to use ‘hasManyThrough’ with more than 3 tables in Laravel (v4+)

as stated in the docs the current hasManyThrough can be used when u have something like

country > users > posts

which we can then use it like

$posts = Country::find(id)->posts;

but what if you have more than that ? like

country > cities > users > posts

// or

country > cities > towns > users > posts

so to achieve that we have 2 options :

1-

stick to hasMany & belongsTo and add foreign ids for all the other models on the “post” model, like

posts
  id - integer
  country_id - foreign
  city_id - foreign
  town_id - foreign
  user_id - foreign
  ...

and call it like

$posts = Post::where('country_id',id)->get();

2-

go with the hasManyThrough on each table except ‘user & post‘ unless you want to go deeper, like

// Country
class Country extends Model
{
  public function cities()
  {
    return $this->hasMany(City::class);
  }

  public function towns()
  {
    return $this->hasManyThrough('App\Town', 'App\City');
  }
}

// City
class City extends Model
{
  public function country()
  {
    return $this->belongsTo(Country::class);
  }

  public function towns()
  {
    return $this->hasMany(Town::class);
  }

  public function users()
  {
    return $this->hasManyThrough('App\User', 'App\Town');
  }
}

// Town
class Town extends Model
{
  public function city()
  {
    return $this->belongsTo(City::class);
  }

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

  public function posts()
  {
    return $this->hasManyThrough('App\Post', 'App\User');
  }
}

// User
class User extends Authenticatable
{
  public function town()
  {
    return $this->belongsTo(Town::class);
  }

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

// Post
class Post extends Model
{
  public function user()
  {
    return $this->belongsTo(User::class);
  }
}

and the tables

countries
  id - integer
  ...

cities
  id - integer
  country_id - foreign
  ...

towns
  id - integer
  city_id - foreign
  ...

users
  id - integer
  town_id - foreign
  ...

posts
  id - integer
  user_id - foreign
  ...

now everything is in place except if we now tried to call

$posts = Country::find(id)->posts;

we would get an error because the country model knows nothing about posts so to fix that, we simply add

// Country Model

// go through each town and get its posts
// merge all returned posts into one array
public function posts()
{
  $posts = [];

  foreach ($this->towns as $town) {
    $posts[] = $town->posts;
  }

  return $posts;
}

and now we can call it like

$posts = Country::find(id)->posts();
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s