2014-09-07 9 views
8

Próbuję uzyskać dostęp do obiektów podrzędnych relacji zagnieżdżonych, które zwracają wiele wyników z obiektu nadrzędnego.Laravel: Wykonywanie zapytań i uzyskiwanie dostępu do obiektów podrzędnych w relacji zagnieżdżonej z klauzulami, gdzie klauzule

Powiedzmy mam 4 modele: Kraj - prowincje - Miasta - Gminy

ich relacje są następujące:

Kraj Model

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

    public function provinces() 
    { 
     return $this->hasMany('Province'); 
    } 
} 

Prowincja model

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

    public function cities() 
    { 
     return $this->hasMany('City'); 
    } 
    public function country() 
    { 
     return $this->belongsTo('Country'); 
    } 
} 

Miasto model

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

    public function municipalities() 
    { 
     return $this->hasMany('Municipality'); 
    } 
    public function province() 
    { 
     return $this->belongsTo('Province'); 
    } 
} 

Gmina model

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

    public function cities() 
    { 
     return $this->belongsTo('City'); 
    } 
} 

Teraz co próbuję zrobić, to wszystkie gminy w danym kraju, które mają populację ponad 9000 i znajdują się w prowincjach które są uważane za Zachód.

tej pory mam coś takiego:

$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); 

Teraz mogę łatwo uzyskać prowincjach $country->provinces ale nie mogę iść głębiej.

EDIT1: Naprawianie relacji belongsTo zauważonej przez Jarka.

EDIT2: Oprócz odpowiedzi Jarka, chciałem podzielić się tym, co również znalazłem, jednak metoda Jarka jest prawdopodobnie bardziej odpowiednią metodą.

Zamiast próbować iść od góry do dołu (kraj -> Gmina) postanowiłem spróbować w inny sposób (gmina -> Country) Oto jak to działa (i przetestowane, działa również)

  $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(); 

Nie mam pojęcia, czy jest to właściwy sposób, czy też występ byłby akceptowany, ale wydawało się, że to dla mnie działka, jednak odpowiedź Jarka wygląda bardziej elegancko.

Odpowiedz

9

Twoja Municipality - City jest prawdopodobnie belongsTo, a nie hasMany jak w wklejeniu.

Zresztą można użyć hasManyThrough zakresie dostępu daleko związanych kolekcję:

Country - City 
Province - Municipality 

niestety nie ma relacja na 3 poziom zagnieżdżenia, więc nie można tego zrobić tak po prostu.


Następnie kod z whereHas nie ogranicza provinces do west i municipalities do 9000+, ale tylko granice countries do tych, które są z nimi związane.W twoim przypadku oznacza to, że wynik będzie wynosił Country (jeśli jego relacje są zgodne z tymi wymaganiami) lub null w przeciwnym razie.

Więc jeśli naprawdę chcesz, aby ograniczyć związane z kolekcjami, to trzeba ten kawałek:

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

ten ubiega chętnych ograniczenia załadunku i co robi to:

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 

skoro” Nie interesują Cię miasta, możesz użyć numeru hasManyThrough w prowincji:

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

następnie:

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

Jednak w obu przypadkach nie można bezpośrednio przejść do gminy, ale tylko tak:

// 1 classic 
$country->provinces->first()->cities->first()->municipalities; 
// 2 hasManyThrough 
$country->provinces->first()->municipalities; 

W takiej sytuacji, jeśli chcesz pracować z wszystkie te gminy, potrzebujesz tej sztuczki:

$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); 

To uruchomi dodatkowe zapytanie, ale teraz wszystkie gminy są w s ingle, flat collection, dzięki czemu praca z nim jest bardzo łatwa. W przeciwnym razie prawdopodobnie skończysz z grupą pętli foreach.

+0

Dziękujemy! Tego właśnie szukałem, ale nie mogłem tego rozgryźć. W tym samym czasie wymyśliłem inny sposób, aby to zrobić i miałam filtrować od dołu. Zaktualizuję moje pytanie, aby odzwierciedlić to, co znalazłem. – sholmes

+0

Chciałbym również zauważyć, że interesujące jest to, że możesz przejść tylko 3 poziomy zagnieżdżania, a teraz, gdy wiem, mogę to obejść. – sholmes

+1

Tak, możesz zrobić to na odwrót używając 'whereHas'. To dobry sposób, zależy tylko od twoich potrzeb. W każdym razie nie rozumiem co masz na myśli przez 3 poziomy zagnieżdżania? –

Powiązane problemy