Laravel:使用where子句查询和访问嵌套关系中的子对象

sho*_*mes 9 nested relationships laravel eloquent

我试图访问嵌套关系的子对象,从父对象返回许多结果.

假设我有4个模型:国家 - 省 - 城市 - 市政

他们的关系如下:

国家模式

class Country extends Eloquent
{
   protected $table = 'countries';

   public function provinces()
   {
       return $this->hasMany('Province');
   }
}
Run Code Online (Sandbox Code Playgroud)

省模型

class Province extends Eloquent
{
   protected $table = 'provinces';

   public function cities()
   {
       return $this->hasMany('City');
   }
   public function country()
   {
       return $this->belongsTo('Country');
   }
}
Run Code Online (Sandbox Code Playgroud)

城市模型

class City extends Eloquent
{
   protected $table = 'cities';

   public function municipalities()
   {
       return $this->hasMany('Municipality');
   }
   public function province()
   {
       return $this->belongsTo('Province');
   }
}
Run Code Online (Sandbox Code Playgroud)

市政模式

class Municipality extends Eloquent
{
   protected $table = 'municipalities';

   public function cities()
   {
      return $this->belongsTo('City');
   }
 }
Run Code Online (Sandbox Code Playgroud)

现在我要做的是让一个拥有超过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); 
Run Code Online (Sandbox Code Playgroud)

现在我可以很容易地获得各省,$country->provinces但我不能更深入.

EDIT1:修复了Jarek注意到的belongsTo关系.

编辑2:除了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();
Run Code Online (Sandbox Code Playgroud)

我不知道这是否是一种实际的正确方式,或者性能是否会被接受,但它似乎对我有用,但Jarek的答案看起来更优雅.

Jar*_*zyk 10

你的Municipality- City很可能belongsTo,hasMany不像粘贴.

无论如何,您可以使用hasManyThrough关系来访问远远相关的集合:

Country - City
Province - Municipality
Run Code Online (Sandbox Code Playgroud)

不幸的是,3级嵌套没有关系,所以你不能这样做.


接下来,您的代码whereHas不限制provinceswestmunicipalities9000+,但只限制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);
Run Code Online (Sandbox Code Playgroud)

这正在应用急切的加载约束,它的作用是:

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
Run Code Online (Sandbox Code Playgroud)

由于您对城市不感兴趣,您可以hasManyThrough在省内使用:

// Province model
public function municipalities()
{
  return $this->hasManyThrough('Municipality', 'City');
}
Run Code Online (Sandbox Code Playgroud)

然后:

$country = Country::with(['provinces' => function($query){
    $query->where('location', 'West');
}, 'provinces.municipalities' => function ($query){
    $query->where('population', '>', 9000);
}])->find($country_id);
Run Code Online (Sandbox Code Playgroud)

但是,在这两种情况下,您都无法直接访问市政当局,但仅限于此:

// 1 classic
$country->provinces->first()->cities->first()->municipalities;
// 2 hasManyThrough
$country->provinces->first()->municipalities;
Run Code Online (Sandbox Code Playgroud)

话虽这么说,如果你想与所有这些城市合作,你需要这个技巧:

$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);
Run Code Online (Sandbox Code Playgroud)

这将运行额外的查询,但现在所有的城市都是单一,扁平的集合,所以它很容易使用.否则你可能会得到一堆foreach循环.