2015-10-26 26 views
5

Penso che questo sia uno schema comune, ma non riesco a trovare l'elegante modo CakePHP di farlo. L'idea è di rimuovere i valori da una lista che è già stata scelta. Per usare un esempio dal libro Cake:Cakephp 3 NOT IN query

table Students (id int, name varchar) 
table Courses (id int, name varchar) 
table CoursesMemberships (id int, student_id int, course_id int, days_attended int, grade varchar) 

Tutto quello che voglio fare è creare una query che restituisce i corsi che un dato studente non ha ancora firmato per, molto probabilmente per popolare una select discesa.

Se non fossi usando torta, mi piacerebbe fare qualcosa di simile a

select * from Courses where id not in 
    (select course_id from CoursesMemberships where student_id = $student_id) 

O forse un equivalente NON ESISTE clausola, o outer join inganno, ma si ottiene l'idea.

Sono perplesso su come farlo elegantemente in Cake. Mi sembra che questo sarebbe un bisogno comune, ma ho cercato per un po ', così come provato alcune idee di query, senza successo.

risposta

2

Trovato IMO la risposta più elegante ... utilizzare l'opzione notMatching():

$data = $this->Courses->find("list") 
         ->notMatching("Students", 
         function($q) use ($student_id) { 
          return $q->where(["Students.id"=>$student_id]); 
         } 
        ); 

Questo presuppone che gli studenti hasMany Corsi e corsi Naturalmente ci sono molti studenti.

Penso che questa sia la risposta più elegante dal momento che non dipende dalla conoscenza di qualsiasi SQL e rappresenta la logica di ciò che sto cercando di ottenere utilizzando solo la semantica di Cake.

5

In CakePHP 3 è possibile creare query NOT IN come questo.

$query = $Courses->find() 
    ->where(['id NOT IN' => $ids]); 

E in CakePHP 3 è possibile creare IN query come questa.

$query = $Courses->find() 
    ->where(['id IN' => $ids]); 

Si può leggere su di esso in CakePHP 3 Cookbook - Creating IN Clauses.

+0

Sono riuscito a farlo funzionare utilizzando un meccanismo più complesso (vedere i commenti sopra). Ma quello che stavo cercando è un modo di fare questa query in un colpo solo, piuttosto che interrogare per i record esistenti, costringerli in un array, quindi usare il precedente. –

+0

vedere questo esempio: http://book.cakephp.org/3.0/en/orm/query-builder.html#subqueries – user3082321

9

Se si desidera utilizzare un subquery, semplicemente passare un oggetto query come il valore della condizione, come

$subquery = $Courses->CoursesMemberships 
    ->find() 
    ->select(['CoursesMemberships.course_id']) 
    ->where(['CoursesMemberships.student_id' => $student_id]); 

$query = $Courses 
    ->find() 
    ->where([ 
     'Courses.id NOT IN' => $subquery 
    ]); 

In alternativa c'è anche Query::notMatching() (come di CakePHP 3.1), che può essere usato per selezionare i record whichs record associati non corrispondono condizioni specifiche:

$query = $Courses 
    ->find() 
    ->notMatching(['CoursesMemberships' => function (\Cake\ORM\Query $query) use ($student_id) { 
     return $query 
      ->where(['CoursesMemberships.student_id' => $student_id]); 
    }]); 

Vedi anche