I am trying to access the child objects of nested relationships that return many results from the parents object.
Let's say I have 4 models : Country - Provinces - Cities - Municipalities
Their relationships are as follows :
Country Model
class Country extends Eloquent
{
protected $table = 'countries';
public function provinces()
{
return $this->hasMany('Province');
}
}
Province Model
class Province extends Eloquent
{
protected $table = 'provinces';
public function cities()
{
return $this->hasMany('City');
}
public function country()
{
return $this->belongsTo('Country');
}
}
City Model
class City extends Eloquent
{
protected $table = 'cities';
public function municipalities()
{
return $this->hasMany('Municipality');
}
public function province()
{
return $this->belongsTo('Province');
}
}
Municipality Model
class Municipality extends Eloquent
{
protected $table = 'municipalities';
public function cities()
{
return $this->belongsTo('City');
}
}
Now what I am trying to do is get all municipalities in a given country that have a population over 9000 and are located in provinces that are considered West.
So far I have something like this :
$country_id = 1;
$country = Country::whereHas('provinces', function($query){
$query->where('location', 'West');
$query->whereHas('cities', function($query){
$query->whereHas('municipalities', function($query){
$query->where('population', '>', 9000);
});
});
})->find($country_id);
Now I can easily get the provinces with $country->provinces
but I can't go any deeper than that.
EDIT1 : Fixing the belongsTo relationship as noticed by Jarek.
EDIT2: In addition to Jarek's answer, I wanted to share what I also found however Jarek's is probably the more proper method.
Instead of trying to go from top to bottom (Country -> Municipality) I decided to try the other way (Municipality -> Country) Here's how it works (and I tested it, also works)
$municipalities = Municipality::where('population', '>', 9000)
->whereHas('city', function($q) use ($country_id){
$q->whereHas('province', function($q) use ($country_id){
$q->where('location', 'West');
$q->whereHas('country', function($q) use ($country_id){
$q->where('id', $country_id);
});
});
})->get();
I have no idea if this is an actual proper way or if performance would be accepted but it seemed to do the trick for me however Jarek's answer looks more elegant.