sql >> Database >  >> RDS >> Mysql

Selecteer Alle Evenementen met Evenement->Schedule->Datum tussen start- en einddatums in CakePHP

In dit soort situaties heb ik de neiging om de associaties van Cake of Containable niet te gebruiken en de joins zelf te maken:

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

Het is een beetje dik, maar het levert precies de zoekopdracht op die ik wil.

UPDATE

Laten we uw code in delen breken en kijken waar we deze kunnen verbeteren. Het eerste deel is de voorbereiding voor de find . Ik heb je code herschreven om hem korter te maken, en dit is wat ik bedacht:

// 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']
}

Merk op dat ik de meeste OK's heb geëlimineerd. Dat komt omdat je een array kunt doorgeven als een waarde in conditions , en Cake maakt er een IN(...) van statement in de SQL-query. Bijvoorbeeld:'Model.field' => array(1,2,3) genereert 'Model.field IN (1,2,3)' . Dit werkt net als OR's, maar vereist minder code. Het bovenstaande codeblok doet dus precies hetzelfde als uw code, maar het is korter.

Nu komt het complexe deel, de find zelf.

Gewoonlijk raad ik de geforceerde joins alleen aan, zonder Containable en met 'recursive'=>false . Ik geloof dat dit meestal is de beste manier om met complexe vondsten om te gaan. Met Associations en Containable voert Cake verschillende SQL-query's uit op de database (één query per model/tabel), wat meestal inefficiënt is. Containable geeft ook niet altijd de verwachte resultaten (zoals je hebt opgemerkt toen je het probeerde).

Maar want in jouw geval zijn er vier ingewikkelde associaties, misschien is een gemengde aanpak de ideale oplossing - anders zou het te ingewikkeld zijn om de dubbele gegevens op te ruimen. (De 4 complexe associaties zijn:Event hasMany Dates [via Event hasMany Schedule, Schedule hasMany Date], Event HABTM EventType, Event HABTM EventSubType, Event HABTM EventSubSubType). Dus we zouden Cake het ophalen van gegevens van EventType, EventSubType en EventSubSubType kunnen laten regelen, om te veel duplicaten te vermijden.

Dus hier is wat ik voorstel:gebruik joins voor alle vereiste filtering, maar neem geen Datum en [Sub[Sub]]Types op in velden. Vanwege de modelassociaties die je hebt, zal Cake automatisch extra query's uitvoeren op de DB om die gegevens op te halen. Geen Containable nodig.

De code:

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

We hebben contains verwijderd , en enkele van de extra zoekopdrachten die Cake daarom uitvoerde. De meeste joins zijn van het type INNER . Dit betekent dat er ten minste één record moet bestaan ​​op beide tabellen die bij de join betrokken zijn, anders krijgt u minder resultaten dan u zou verwachten. Ik neem aan dat elk evenement plaatsvindt in een restaurant OF een locatie, maar niet beide, daarom heb ik LEFT . gebruikt voor die tafels (en steden). Als sommige van de velden die in de samenvoegingen worden gebruikt optioneel zijn, moet u LEFT . gebruiken in plaats van INNER op de gerelateerde joins.

Als we 'recursive'=>false gebruikten hier zouden we nog steeds de juiste gebeurtenissen krijgen en geen herhaling van gegevens, maar datums en [Sub[Sub]]Types zouden ontbreken. Met de 2 recursieniveaus doorloopt Cake automatisch de geretourneerde gebeurtenissen en voor elke gebeurtenis voert het de nodige query's uit om de bijbehorende modelgegevens op te halen.

Dit is bijna wat je aan het doen was, maar zonder Containable, en met een paar extra tweaks. Ik weet dat het nog steeds een lang, lelijk en saai stuk code is, maar er zijn tenslotte 13 databasetabellen bij betrokken...

Dit is allemaal niet-geteste code, maar ik denk dat het zou moeten werken.



  1. Hoe parallelle plannen opstarten - deel 2

  2. INT-vergelijking zonder WHERE-instructie

  3. Hoe orakelindexen kiezen en optimaliseren?

  4. Hoe een standalone Moodle te migreren naar een schaalbare configuratie van een geclusterde database