Mysql
 sql >> Datenbank >  >> RDS >> Mysql

Wählen Sie Alle Events mit Event->Zeitplan->Datum zwischen Start- und Enddatum in CakePHP

In solchen Situationen neige ich dazu, die Assoziationen von Cake oder Containable nicht zu verwenden und die Joins selbst zu erstellen:

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

Es ist ein bisschen klobig, aber führt zu genau der Abfrage, die ich will.

AKTUALISIEREN

Lassen Sie uns Ihren Code in Teile zerlegen und sehen, wo wir ihn verbessern können. Der erste Teil ist die Vorbereitung für das find . Ich habe Ihren Code neu geschrieben, um ihn kürzer zu machen, und das ist dabei herausgekommen:

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

Beachten Sie, dass ich die meisten ORs eliminiert habe. Das liegt daran, dass Sie ein Array als Wert in conditions übergeben können , und Cake macht es zu einem IN(...) Anweisung in der SQL-Abfrage. Zum Beispiel:'Model.field' => array(1,2,3) erzeugt 'Model.field IN (1,2,3)' . Dies funktioniert genauso wie ORs, erfordert aber weniger Code. Der obige Codeblock macht also genau dasselbe wie Ihr Code, aber er ist kürzer.

Jetzt kommt der komplexe Teil, das find selbst.

Normalerweise würde ich die erzwungenen Joins allein empfehlen, ohne Containable und mit 'recursive'=>false . Ich glaube das normalerweise ist der beste Weg, um mit komplexen Funden umzugehen. Mit Associations und Containable führt Cake mehrere SQL-Abfragen gegen die Datenbank aus (eine Abfrage pro Modell/Tabelle), was tendenziell ineffizient ist. Außerdem liefert Containable nicht immer die erwarteten Ergebnisse (wie Sie beim Ausprobieren bemerkt haben).

Aber denn in Ihrem Fall sind es vier komplexen Assoziationen, vielleicht ist ein gemischter Ansatz die ideale Lösung - andernfalls wäre es zu kompliziert, die doppelten Daten zu bereinigen. (Die 4 komplexen Assoziationen sind:Event hasMany Dates [bis Event hasMany Schedule, Schedule hasMany Date], Event HABTM EventType, Event HABTM EventSubType, Event HABTM EventSubSubType). So könnten wir Cake den Datenabruf von EventType, EventSubType und EventSubSubType überlassen, um zu viele Duplikate zu vermeiden.

Ich schlage also Folgendes vor:Verwenden Sie Verknüpfungen für alle erforderlichen Filter, aber schließen Sie Datum und [Sub[Sub]]Types nicht in die Felder ein. Aufgrund der Modellzuordnungen, die Sie haben, führt Cake automatisch zusätzliche Abfragen für die DB aus, um diese Datenbits abzurufen. Kein Behälter erforderlich.

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

Wir haben contains eliminiert , und einige der zusätzlichen Abfragen, die Cake deswegen ausgeführt hat. Die meisten Joins sind vom Typ INNER . Das bedeutet, dass in beiden am Join beteiligten Tabellen mindestens ein Datensatz vorhanden sein muss, sonst erhalten Sie weniger Ergebnisse als erwartet. Ich gehe davon aus, dass jede Veranstaltung in einem Restaurant ODER stattfindet ein Veranstaltungsort, aber nicht beides, deshalb habe ich LEFT verwendet für diese Tabellen (und Städte). Wenn einige der in den Joins verwendeten Felder optional sind, sollten Sie LEFT verwenden statt INNER auf den zugehörigen Joins.

Wenn wir 'recursive'=>false verwendet haben hier würden wir immer noch die richtigen Ereignisse und keine Datenwiederholung erhalten, aber Daten und [Sub[Sub]]Types würden fehlen. Mit den 2 Rekursionsebenen durchläuft Cake automatisch die zurückgegebenen Ereignisse und führt für jedes Ereignis die erforderlichen Abfragen aus, um die zugehörigen Modelldaten abzurufen.

Das ist fast das, was Sie getan haben, aber ohne Containable und mit ein paar zusätzlichen Optimierungen. Ich weiß, es ist immer noch ein langes, hässliches und langweiliges Stück Code, aber immerhin sind 13 Datenbanktabellen beteiligt ...

Dies ist alles ungetesteter Code, aber ich glaube, es sollte funktionieren.