我正在尝试访问嵌套关系的子对象,这些对象从父对象返回许多结果。
假设我有4个模型:国家 - 省 - 城市 - 市政
他们的关系如下:
国家/地区模型
class Country extends Eloquent
{
protected $table = 'countries';
public function provinces()
{
return $this->hasMany('Province');
}
}
省份模型
class Province extends Eloquent
{
protected $table = 'provinces';
public function cities()
{
return $this->hasMany('City');
}
public function country()
{
return $this->belongsTo('Country');
}
}
城市模型
class City extends Eloquent
{
protected $table = 'cities';
public function municipalities()
{
return $this->hasMany('Municipality');
}
public function province()
{
return $this->belongsTo('Province');
}
}
市政模式
class Municipality extends Eloquent
{
protected $table = 'municipalities';
public function cities()
{
return $this->belongsTo('City');
}
}
现在我要做的是让一个人口超过9000的特定国家的所有城市都位于被认为是西部的省份。
到目前为止,我有这样的事情:
$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);
现在我可以很容易地通过$country->provinces
获得省份,但我不能更深入。
EDIT1:修复了Jarek注意到的belongsTo关系。
EDIT2:除了Jarek的答案之外,我想分享我也发现的内容,但Jarek可能是更合适的方法。
而不是试图从上到下(国家 - >市)我决定尝试另一种方式(市政 - >国家)这是它的工作方式(我测试了它,也有效)
$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();
我不知道这是否是一种实际的正确方式,或者性能是否会被接受,但它似乎对我有用,但Jarek的回答看起来更优雅。
答案 0 :(得分:10)
您的Municipality
- City
可能是belongsTo
,而不是粘贴中的hasMany
。
无论如何,您可以使用hasManyThrough
关系访问远远相关的集合:
Country - City
Province - Municipality
不幸的是,3级嵌套没有关系,所以你不能这样做。
接下来,使用whereHas
的代码不会将provinces
限制为west
,将municipalities
限制为9000+
,但仅将countries
限制为Country
,与他们有关。在您的情况下,这意味着结果将是null
(如果其关系符合这些要求)或$country = Country::with(['provinces' => function($query){
$query->where('location', 'West');
}, 'provinces.cities.municipalities' => function ($query){
$query->where('population', '>', 9000);
}])->find($country_id);
否则。
因此,如果你真的想限制相关的集合,那么你需要这个:
1. loads only West provinces for country with id 1
2. loads all the cities in these provinces
3. loads only 9k+ municipalities in these cities
这正在应用急切的加载限制,它的作用是:
hasManyThrough
由于您对城市不感兴趣,因此您可以在省内使用// Province model
public function municipalities()
{
return $this->hasManyThrough('Municipality', 'City');
}
:
$country = Country::with(['provinces' => function($query){
$query->where('location', 'West');
}, 'provinces.municipalities' => function ($query){
$query->where('population', '>', 9000);
}])->find($country_id);
然后:
// 1 classic
$country->provinces->first()->cities->first()->municipalities;
// 2 hasManyThrough
$country->provinces->first()->municipalities;
然而,在这两种情况下,您都无法直接访问市政当局,但仅限于此:
$country = Country::with(['provinces' => function($query){
$query->where('location', 'West');
}, 'provinces.municipalities' => function ($query) use (&$municipalities) {
// notice $municipalities is passed by reference to the closure
// and the $query is executed using ->get()
$municipalities = $query->where('population', '>', 9000)->get();
}])->find($country_id);
话虽如此,如果你想与所有这些城市合作,你需要这个技巧:
{{1}}
这将运行额外的查询,但现在所有的城市都是单一的,扁平的集合,所以它很容易使用。否则你最终可能会遇到一堆foreach循环。