Mysql
 sql >> Database >  >> RDS >> Mysql

Seleziona Tutti gli eventi con Evento->Programma->Data tra le date di inizio e fine in CakePHP

In questo tipo di situazione, tendo a non usare le associazioni di Cake, o Containable, e creo io stesso i join:

$events = $this->Event->find('all', array(
    'joins'=>array(
        array(
            'table' => $this->Schedule->table, 
            'alias' => 'Schedule', 
            'type' => 'INNER', 
            'foreignKey' => false,
            'conditions'=> array(
                'Schedule.event_id = Event.id',
            ),
        ),
        array(
            'table' => $this->Date->table, 
            'alias' => 'Date', 
            'type' => 'INNER', 
            'foreignKey' => false,
            'conditions'=> array(
                'Date.schedule_id = Schedule.id',
            ),
        ),
    ),
    'conditions'=>array(
        'Date.start >=' => $start_date,
        'Date.start <=' => $end_date,
    ),
    'order'=>'Event.created DESC',
    'limit'=>5
));

È un po' grosso, ma risulta nella query esatta che desidero.

AGGIORNAMENTO

Rompiamo il tuo codice in parti e vediamo dove potremmo migliorarlo. La prima parte è la preparazione per il find . Ho riscritto il tuo codice cercando di renderlo più breve, e questo è quello che mi è venuto in mente:

// Default options go here
$defaultOpts = array(
    'start' => date('Y-m-d') . ' 00:00:00',
    'end' => date('Y-m-d') . ' 23:59:59',
    'limit' => 10
)

// Use default options if nothing is passed, otherwise merge passed options with defaults
$opts = is_array($opts) ? array_merge($defaultOpts, $opts) : $defaultOpts;

// Initialize array to hold query conditions
$conditions = array();

//date conditions
$conditions[] = array(
    "Date.start >=" => $qOpts['start'],
    "Date.start <=" => $qOpts['end'],
));

//cities conditions
if(isset($opts['cities']) && is_array($opts['cities'])) {
    $conditions['OR'] = array();
    $conditions['OR'][] = array('Venue.city_id'=>$opts['cities']);
    $conditions['OR'][] = array('Restaurant.city_id'=>$opts['cities']);
}

//event types conditions
//$opts['event_types'] = array('1');
if(isset($opts['event_types']) && is_array($opts['event_types'])) {
    $conditions[] = 'EventTypesEvents.event_type_id' => $opts['event_types']
}

//event sub types conditions
if(isset($opts['event_sub_types']) && is_array($opts['event_sub_types'])) {
    $conditions[] = 'EventSubTypesEvents.event_sub_type_id' => $opts['event_sub_types']
}

//event sub sub types conditions
if(isset($opts['event_sub_types']) && is_array($opts['event_sub_sub_types'])) {
    $conditions[] = 'EventSubSubTypesEvents.event_sub_sub_type_id' => $opts['event_sub_sub_types']
}

Si noti che ho eliminato la maggior parte degli OR. Questo perché puoi passare un array come valore in conditions e Cake lo trasformerà in un IN(...) istruzione nella query SQL. Ad esempio:'Model.field' => array(1,2,3) genera 'Model.field IN (1,2,3)' . Funziona proprio come gli OR, ma richiede meno codice. Quindi il blocco di codice sopra fa esattamente lo stesso che stava facendo il tuo codice, ma è più breve.

Ora arriva la parte complessa, il find stesso.

Di solito consiglierei i join forzati da soli, senza Containable e con 'recursive'=>false . Credo che questo solitamente è il modo migliore per affrontare reperti complessi. Con Associazioni e Containable, Cake esegue diverse query SQL sul database (una query per modello/tabella), che tende a essere inefficiente. Inoltre, Containable non restituisce sempre i risultati attesi (come hai notato quando l'hai provato).

Ma poiché nel tuo caso sono quattro complesse associazioni coinvolte, forse un approccio misto sarà la soluzione ideale, altrimenti sarebbe troppo complicato ripulire i dati duplicati. (Le 4 associazioni complesse sono:Event has Many Dates [attraverso Event has Many Schedule, Schedule has Many Date], Event HABTM EventType, Event HABTM EventSubType, Event HABTM EventSubSubType). Quindi, potremmo lasciare che Cake gestisca il recupero dei dati di EventType, EventSubType ed EventSubSubType, evitando troppi duplicati.

Quindi ecco cosa suggerisco:usa i join per tutti i filtri richiesti, ma non includere Date e [Sub[Sub]]Types nei campi. A causa delle associazioni di modelli che hai, Cake eseguirà automaticamente query aggiuntive sul DB per recuperare quei bit di dati. Non è necessario contenere.

Il codice:

// We already fetch the data from these 2 models through
// joins + fields, so we can unbind them for the next find,
// avoiding extra unnecessary queries. 
$this->unbindModel(array('belongsTo'=>array('Restaurant', 'Venue'));

$data = $this->find('all', array(
    // The other fields required will be added by Cake later
    'fields' => "
        Event.*, 
        Restaurant.id, Restaurant.name, Restaurant.slug, Restaurant.address, Restaurant.GPS_Lon, Restaurant.GPS_Lat, Restaurant.city_id,
        Venue.id, Venue.name, Venue.slug, Venue.address, Venue.GPS_Lon, Venue.GPS_Lat, Venue.city_id,
        City.id, City.name, City.url_name
    ",  
    'joins' => array(
        array(
            'table' => $this->Schedule->table,
            'alias' => 'Schedule',
            'type' => 'INNER',
            'foreignKey' => false,
            'conditions' => 'Schedule.event_id = Event.id',
        ),
        array(
            'table' => $this->Schedule->Date->table,
            'alias' => 'Date',
            'type' => 'INNER',
            'foreignKey' => false,
            'conditions' => 'Date.schedule_id = Schedule.id',
        ),
        array(
            'table' => $this->EventTypesEvent->table,
            'alias' => 'EventTypesEvents',
            'type' => 'INNER',
            'foreignKey' => false,
            'conditions' => 'EventTypesEvents.event_id = Event.id',
        ),
        array(
            'table' => $this->EventSubSubTypesEvent->table,
            'alias' => 'EventSubSubTypesEvents',
            'type' => 'INNER',
            'foreignKey' => false,
            'conditions' => 'EventSubSubTypesEvents.event_id = Event.id',
        ),
        array(
            'table' => $this->Restaurant->table,
            'alias' => 'Restaurant',
            'type' => 'LEFT',
            'foreignKey' => false,
            'conditions' => 'Event.restaurant_id = Restaurant.id',
        ),
        array(
            'table' => $this->City->table,
            'alias' => 'RestaurantCity',
            'type' => 'LEFT',
            'foreignKey' => false,
            'conditions' => 'Restaurant.city_id = city.id',
        ),
        array(
            'table' => $this->Venue->table,
            'alias' => 'Venue',
            'type' => 'LEFT',
            'foreignKey' => false,
            'conditions' => 'Event.venue_id = Venue.id',
        ),
        array(
            'table' => $this->City->table,
            'alias' => 'VenueCity',
            'type' => 'LEFT',
            'foreignKey' => false,
            'conditions' => 'Venue.city_id = city.id',
        ),
    ),
    'conditions' => $conditions,
    'limit' => $opts['limit'],
    'recursive' => 2
));

Abbiamo eliminato contains , e alcune delle query extra che Cake stava eseguendo a causa di ciò. La maggior parte dei join sono di tipo INNER . Ciò significa che deve esistere almeno un record su entrambe le tabelle coinvolte nel join, altrimenti otterrai meno risultati di quelli che ti aspetteresti. Presumo che ogni evento si svolga in un ristorante O un luogo, ma non entrambi, ecco perché ho usato LEFT per quei tavoli (e città). Se alcuni dei campi utilizzati nei join sono facoltativi, dovresti utilizzare LEFT invece di INNER sui relativi join.

Se abbiamo usato 'recursive'=>false qui, otterremmo comunque gli eventi giusti e nessuna ripetizione dei dati, ma mancherebbero le date e i tipi [Sub[Sub]]. Con i 2 livelli di ricorsione, Cake scorrerà automaticamente gli eventi restituiti e per ogni evento eseguirà le query necessarie per recuperare i dati del modello associati.

Questo è quasi quello che stavi facendo, ma senza Containable e con alcune modifiche extra. So che è ancora un pezzo di codice lungo, brutto e noioso, ma dopotutto ci sono 13 tabelle di database coinvolte...

Questo è tutto codice non testato, ma credo che dovrebbe funzionare.