2014-09-07 18 views
8

Sto provando ad accedere agli oggetti figlio di relazioni nidificate che restituiscono molti risultati dall'oggetto genitori.Laravel: interrogazione e accesso agli oggetti figlio nella relazione nidificata con clausole where

Diciamo che ho 4 modelli: Paese - Province - Città - Comuni

I loro rapporti sono i seguenti:

Nazione Modello

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

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

Provincia Modello

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

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

Città Modello

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

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

Comune Modello

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

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

Ora quello che sto cercando di fare è ottenere tutti i comuni in un determinato paese che hanno una popolazione di oltre 9000 e si trovano in province che sono considerati occidentali.

Finora ho qualcosa di simile:

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

ora posso facilmente ottenere le province con $country->provinces ma non posso andare più profondo di quello.

MODIFICA 1: correzione della relazione appartiene a Jarek.

EDIT2: oltre alla risposta di Jarek, volevo condividere ciò che ho trovato, tuttavia Jarek è probabilmente il metodo più appropriato.

Invece di cercare di andare da cima a fondo (Paese -> Comune) ho deciso di provare il contrario (Comune -> Paese) Ecco come funziona (e ho provato, funziona anche)

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

Non ho idea se questo è un modo vero e proprio o se le prestazioni sarebbero state accettate, ma mi è sembrato il trucco per me, ma la risposta di Jarek sembra più elegante.

risposta

9

Il tuo Municipality - City è probabilmente belongsTo, non hasMany come nella pasta.

In ogni caso è possibile utilizzare hasManyThrough relazione all'accesso collezione molto legati:

Country - City 
Province - Municipality 

purtroppo non v'è alcuna relazione per 3 livello di nidificazione, quindi non è possibile fare questo proprio così.


Avanti, il codice con whereHas non limita provinces-west e municipalities-9000+, ma solo limiti countries a coloro, che sono legati a loro.Nel tuo caso questo significa che il risultato sarà Country (se le sue relazioni corrispondono a questi requisiti) o null in caso contrario.

Quindi, se si vuole veramente limitare collezioni correlate, allora avete bisogno di questo pezzo:

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

Questa è l'applicazione dei vincoli eager loading, e ciò che fa è:

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 

Dal momento che si' non interessato alle città, è possibile utilizzare hasManyThrough sulla provincia:

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

poi:

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

Tuttavia, in entrambi i casi non è possibile accedere alle municipalità direttamente, ma solo in questo modo:

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

Detto questo, se si desidera lavorare con tutti quei comuni, è necessario questo trucco:

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

Questo eseguirà query aggiuntive, ma ora tutti i comuni sono in s ingle, collezione flat, quindi è molto facile lavorare con. Altrimenti probabilmente finirai con un mucchio di loop foreach.

+0

Grazie! Questo è quello che stavo cercando ma non riuscivo a capirlo. Allo stesso tempo ho anche trovato un altro modo per farlo ed era quello di filtrare dal basso verso l'alto. Aggiornerò la mia domanda per riflettere ciò che ho trovato. – sholmes

+0

Volevo anche notare che è interessante che tu possa andare solo a 3 livelli di nidificazione e ora che so, posso aggirarlo. – sholmes

+1

Sì, puoi farlo inverso usando 'whereHas'. Questo è un buon modo, dipende solo dalle tue esigenze. Ad ogni modo non capisco cosa intendi con 3 livelli di nidificazione? –