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

Vælg Alle begivenheder med begivenhed->Tidsplan->Dato mellem start- og slutdatoer i CakePHP

I denne slags situationer har jeg en tendens til ikke at bruge Cakes associationer eller Containable og lave sammenføjningerne selv:

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

Det er lidt tykt, men resulterer i den nøjagtige forespørgsel, jeg vil have.

OPDATERING

Lad os bryde din kode op i dele og se, hvor vi kan forbedre den. Den første del er forberedelsen til find . Jeg har omskrevet din kode i forsøget på at gøre den kortere, og dette er, hvad jeg fandt på:

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

Læg mærke til, at jeg eliminerede de fleste af yderområderne. Det er fordi du kan sende et array som en værdi i conditions , og Cake vil gøre det til en IN(...) sætning i SQL-forespørgslen. For eksempel:'Model.field' => array(1,2,3) genererer 'Model.field IN (1,2,3)' . Dette fungerer ligesom OR'er, men kræver mindre kode. Så kodeblokken ovenfor gør præcis det samme som din kode gjorde, men den er kortere.

Nu kommer den komplekse del, find sig selv.

Normalt vil jeg anbefale de tvungne joinforbindelser alene, uden Containable og med 'recursive'=>false . Det tror jeg normalt på er den bedste måde at håndtere komplekse fund på. Med Associations og Containable kører Cake adskillige SQL-forespørgsler mod databasen (én forespørgsel pr. model/tabel), hvilket har en tendens til at være ineffektivt. Desuden returnerer Containable ikke altid de forventede resultater (som du bemærkede, da du prøvede det).

Men da der i dit tilfælde er fire komplekse foreninger involveret, måske vil en blandet tilgang være den ideelle løsning - ellers ville det være for kompliceret at rydde op i duplikatdata. (De 4 komplekse associationer er:Event hasMany Dates [gennem Event hasMany Schedule, Schedule hasMany Date], Event HABTM EventType, Event HABTM EventSubType, Event HABTM EventSubSubType). Så vi kunne lade Cake håndtere datahentning af EventType, EventSubType og EventSubSubType og undgå for mange dubletter.

Så her er, hvad jeg foreslår:Brug joinforbindelser til al den påkrævede filtrering, men medtag ikke Dato og [Sub[Sub]]Typer i felterne. På grund af de modeltilknytninger, du har, vil Cake automatisk køre ekstra forespørgsler mod DB'en for at hente disse data. Der kræves ingen Containable.

Koden:

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

Vi har fjernet contains , og nogle af de ekstra forespørgsler, Cake kørte på grund af det. De fleste joinforbindelser er af typen INNER . Det betyder, at der skal eksistere mindst én post på begge tabeller, der er involveret i joinforbindelsen, ellers får du færre resultater, end du ville forvente. Jeg antager, at hver begivenhed finder sted på en restaurant ELLER et mødested, men ikke begge dele, derfor brugte jeg LEFT for disse borde (og byer). Hvis nogle af felterne, der bruges i joins, er valgfrie, skal du bruge LEFT i stedet for INNER på de relaterede joinforbindelser.

Hvis vi brugte 'recursive'=>false her ville vi stadig få de rigtige begivenheder og ingen datagentagelse, men datoer og [Sub[Sub]]typer ville mangle. Med de 2 niveauer af rekursion vil Cake automatisk gå gennem de returnerede hændelser, og for hver hændelse vil den køre de nødvendige forespørgsler for at hente de tilknyttede modeldata.

Dette er næsten, hvad du gjorde, men uden Containable, og med et par ekstra tweaks. Jeg ved godt, at det stadig er et langt, grimt og kedeligt stykke kode, men der er trods alt 13 databasetabeller involveret...

Dette er alt sammen utestet kode, men jeg tror, ​​det burde virke.



  1. Intelligent MySQL GROUP BY til aktivitetsstreams

  2. MySQL Group Resultater efter dag ved hjælp af tidsstempel

  3. Kan jeg parametrisere tabelnavnet i en forberedt erklæring?

  4. Hvordan vælger man første og sidste datarække fra et mysql-resultat?