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

时间:2014-09-07 20:02:28

标签: laravel nested eloquent relationships

我正在尝试访问嵌套关系的子对象,这些对象从父对象返回许多结果。

假设我有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的回答看起来更优雅。

1 个答案:

答案 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循环。