Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

Selecione Todos os Eventos com Evento->Schedule->Data entre as datas de início e término no CakePHP


Nesse tipo de situação, costumo não usar as associações do Cake, ou Containable, e criar as junções eu mesmo:
$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
));

É um pouco pesado, mas resulta na consulta exata que eu quero.

ATUALIZAÇÃO

Vamos dividir seu código em partes e ver onde podemos melhorá-lo. A primeira parte é a preparação para o find . Eu reescrevi seu código tentando torná-lo mais curto, e foi isso que eu criei:
// 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']
}

Observe que eu eliminei a maioria dos ORs. Isso porque você pode passar um array como um valor em conditions , e o Cake o tornará um IN(...) instrução na consulta SQL. Por exemplo:'Model.field' => array(1,2,3) gera 'Model.field IN (1,2,3)' . Isso funciona como ORs, mas requer menos código. Portanto, o bloco de código acima faz exatamente o mesmo que seu código estava fazendo, mas é mais curto.

Agora vem a parte complexa, o find em si.

Normalmente, eu recomendaria as junções forçadas sozinhas, sem Containable e com 'recursive'=>false . Acredito que geralmente é a melhor maneira de lidar com achados complexos. Com Associações e Contêiníveis, o Cake executa várias consultas SQL no banco de dados (uma consulta por Modelo/tabela), o que tende a ser ineficiente. Além disso, Containable nem sempre retorna os resultados esperados (como você notou quando tentou).

Mas já que no seu caso existem quatro associações complexas envolvidas, talvez uma abordagem mista seja a solução ideal - caso contrário, seria muito complicado limpar os dados duplicados. (As 4 associações complexas são:Event hasMany Dates [através de Event hasMany Schedule, Schedule hasMany Date], Event HABTM EventType, Event HABTM EventSubType, Event HABTM EventSubSubType). Assim, poderíamos deixar o Cake lidar com a recuperação de dados de EventType, EventSubType e EventSubSubType, evitando muitas duplicatas.

Então aqui está o que eu sugiro:use joins para toda a filtragem necessária, mas não inclua Date e [Sub[Sub]]Types nos campos. Por causa das associações de modelo que você tem, o Cake executará automaticamente consultas extras no banco de dados para buscar esses bits de dados. Não é necessário Contêiner.

O 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 , e algumas das consultas extras que o Cake estava executando por causa disso. A maioria das junções são do tipo INNER . Isso significa que pelo menos um registro deve existir em ambas as tabelas envolvidas na junção, ou você obterá menos resultados do que o esperado. Presumo que cada evento ocorra em um restaurante OU um local, mas não ambos, por isso usei LEFT para essas tabelas (e cidades). Se alguns dos campos usados ​​nas junções forem opcionais, você deve usar LEFT em vez de INNER nas junções relacionadas.

Se usarmos 'recursive'=>false aqui, ainda obteríamos os eventos certos e nenhuma repetição de dados, mas datas e [Sub[Sub]]tipos estariam ausentes. Com os 2 níveis de recursão, o Cake automaticamente fará um loop pelos eventos retornados e, para cada evento, ele executará as consultas necessárias para buscar os dados do modelo associado.

Isso é quase o que você estava fazendo, mas sem o Containable e com alguns ajustes extras. Eu sei que ainda é um código longo, feio e chato, mas afinal são 13 tabelas de banco de dados envolvidas...

Este é todo o código não testado, mas acredito que deve funcionar.