sql >> Base de Datos >  >> RDS >> Mysql

Seleccione Todos los eventos con Event->Schedule->Date entre las fechas de inicio y finalización en CakePHP

En este tipo de situación, tiendo a no usar las asociaciones de Cake o Containable, y elaboro las uniones yo mismo:

$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 un poco grueso, pero da como resultado la consulta exacta que quiero.

ACTUALIZAR

Dividamos su código en partes y veamos dónde podemos mejorarlo. La primera parte es la preparación para el find . He reescrito tu código tratando de acortarlo, y esto es lo que se me ocurrió:

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

Tenga en cuenta que eliminé la mayoría de los OR. Eso es porque puedes pasar una matriz como un valor en conditions , y Cake lo convertirá en IN(...) declaración en la consulta SQL. Por ejemplo:'Model.field' => array(1,2,3) genera 'Model.field IN (1,2,3)' . Esto funciona igual que los OR, pero requiere menos código. Así que el bloque de código de arriba hace exactamente lo mismo que estaba haciendo tu código, pero es más corto.

Ahora viene la parte compleja, el find mismo.

Por lo general, recomendaría las uniones forzadas solas, sin Containable y con 'recursive'=>false . Creo que esto normalmente es la mejor manera de lidiar con hallazgos complejos. Con Asociaciones y Contenible, Cake ejecuta varias consultas SQL contra la base de datos (una consulta por Modelo/tabla), lo que tiende a ser ineficiente. Además, Containable no siempre devuelve los resultados esperados (como notó cuando lo probó).

Pero ya que en tu caso hay cuatro asociaciones complejas involucradas, tal vez un enfoque mixto sea la solución ideal; de lo contrario, sería demasiado complicado limpiar los datos duplicados. (Las 4 asociaciones complejas son:Event hasMany Dates [a través de Event hasMany Schedule, Schedule hasMany Date], Event HABTM EventType, Event HABTM EventSubType, Event HABTM EventSubSubType). Entonces, podríamos dejar que Cake maneje la recuperación de datos de EventType, EventSubType y EventSubSubType, evitando demasiados duplicados.

Entonces, esto es lo que sugiero:use uniones para todos los filtros requeridos, pero no incluya Fecha y [Sub[Sub]]Tipos en los campos. Debido a las asociaciones de modelos que tiene, Cake ejecutará automáticamente consultas adicionales en la base de datos para obtener esos bits de datos. No se necesita Contenible.

El código:

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

Eliminamos contains , y algunas de las consultas adicionales que Cake estaba ejecutando debido a ello. La mayoría de las uniones son del tipo INNER . Esto significa que debe existir al menos un registro en ambas tablas involucradas en la combinación, o obtendrá menos resultados de los esperados. Asumo que cada Evento tiene lugar en un Restaurante O un lugar, pero no ambos, por eso usé LEFT para esas tablas (y ciudades). Si algunos de los campos usados ​​en las uniones son opcionales, debe usar LEFT en lugar de INNER en las uniones relacionadas.

Si usamos 'recursive'=>false aquí, todavía obtendríamos los eventos correctos y no se repetirían los datos, pero faltarían las fechas y los [Sub[Sub]]Tipos. Con los 2 niveles de recursividad, Cake recorrerá automáticamente los eventos devueltos y, para cada evento, ejecutará las consultas necesarias para obtener los datos del modelo asociado.

Esto es casi lo que estabas haciendo, pero sin Containable y con algunos ajustes adicionales. Sé que sigue siendo un código largo, feo y aburrido, pero después de todo, hay 13 tablas de base de datos involucradas...

Todo esto es código no probado, pero creo que debería funcionar.