Выберите Все события с Событием-> Расписание-> Дата между начальной и конечной датами в CakePHP - PullRequest
1 голос
/ 16 июня 2011

Попытка выяснить, как построить запрос в CakePHP, где я могу выбрать все события между датами X и Y (даты, введенные пользователем).

Проблема заключается в том, что Event недаты в таблице отсутствуют.

Event hasMany Schedule
Schedule belongsTo Event

Schedule hasMany Date
Date belongsTo Schedule
  • Events table: сведения о событии - название, местоположение, описание ... и т. д.
  • Schedules table: началои дата окончания с опциями повторения
  • Dates table: фактические даты события, созданные из данных в Schedules

Итак, мне действительно нужно выбрать любые события, которые имеютхотя бы одна запись даты между датами X и Y.

Мне также нужно иметь возможность отображать даты с данными события.


Редактировать (ПЕРЕСМОТРЕНО):

Я пробовал это, но похоже, что он извлекает события независимо от даты, но извлекает информацию о дате, только если дата попадает в диапазон:

$this->Event->Behaviors->attach('Containable');
$events = $this->Event->find('all', array(
    'limit'=>5,
    'order'=>'Event.created DESC',
    'contain' => array(
    'Schedule' => array(
        'fields'=>array(),
        'Date' => array(
            'conditions'=>array(
                'start >=' => $start_date,
                'start <=' => $end_date,
                )
            )
        )
    ),
));

* Просто чтобы уточнить - Date.start и Date.end всегда являются одной и той же датой - они просто также включают время (оба датыполя etime) - следовательно, поэтому я проверяю «start» для обоих.


Я пытался использоватьableable, я пытался unbind / bindModel..etc - я должен что-то делать неправильно илине в курсе.

Что следует иметь в виду - как только я выясню, как получить События на основе Даты, мне также нужно добавить другие условия, такие как Типы событий и другие, - не уверен, повлияет ли это на это.ответ (ы) или нет.


ОБНОВЛЕНИЕ:

Вот то, что я использую, что, кажется, работает - также кажется очень уродливым - какие-либо мысли?:

function getEvents($opts = null) {
    //$opts = limit, start(date), end(date), types, subtypes, subsubtypes, cities

    $qOpts['conditions'] = array();

    //dates
    $qOpts['start'] = date('Y-m-d') . ' 00:00:00';
    if(isset($opts['start'])) $qOpts['start'] = $opts['start'];

    $qOpts['end'] = date('Y-m-d') . ' 23:59:59';
    if(isset($opts['end'])) $qOpts['end'] = $opts['end'];

    //limit
    $qOpts['limit'] = 10;
    if(isset($opts['limit'])) $qOpts['limit'] = $opts['limit'];

    //fields
    //$qOpts['fields'] = array('Event.id', 'Event.name', 'Event.slug', 'City.name', 'Date.start');  
    // if(isset($opts['fields'])) $qOpts['fields'] = $opts['fields'];


    //date conditions
    array_push($qOpts['conditions'], array(
        "Date.start >=" => $qOpts['start'],
        "Date.start <=" => $qOpts['end'],
    ));

    //cities conditions
    if(isset($opts['cities'])) {
        if(is_array($opts['cities'])) {
            $cityConditions['OR'] = array();
            foreach($opts['cities'] as $city_id) {
                array_push($cityConditions['OR'], array('OR'=>array('Venue.city_id'=>$city_id, 'Restaurant.city_id'=>$city_id)));
            }
            array_push($qOpts['conditions'], $cityConditions);
        }
    }

    //event types conditions
    //$opts['event_types'] = array('1');
    if(isset($opts['event_types'])) {
        if(is_array($opts['event_types'])) {
            $eventTypeConditions['OR'] = array();
            foreach($opts['event_types'] as $event_type_id) {
                array_push($eventTypeConditions['OR'], array('EventTypesEvents.event_type_id' => $event_type_id));
            }
            array_push($qOpts['conditions'], $eventTypeConditions);
        }
    }

    //event sub types conditions
    if(isset($opts['event_sub_types'])) {
        if(is_array($opts['event_sub_types'])) {
            $eventSubTypeConditions['OR'] = array();
            foreach($opts['event_sub_types'] as $event_sub_type_id) {
                array_push($eventSubTypeConditions['OR'], array('EventSubTypesEvents.event_sub_type_id' => $event_sub_type_id));
            }
            array_push($qOpts['conditions'], $eventSubTypeConditions);
        }
    }

    //event sub sub types conditions
    if(isset($opts['event_sub_sub_types'])) {
        if(is_array($opts['event_sub_sub_types'])) {
            $eventSubSubTypeConditions['OR'] = array();
            foreach($opts['event_sub_sub_types'] as $event_sub_sub_type_id) {
                array_push($eventSubSubTypeConditions['OR'], array('EventSubSubTypesEvents.event_sub_sub_type_id' => $event_sub_sub_type_id));
            }
            array_push($qOpts['conditions'], $eventSubSubTypeConditions);
        }
    }


    $this->recursive = 2;

    $data = $this->find('all', array(
        'contain' => array(
            'Restaurant' => array(
                'fields' => array('id', 'name', 'slug', 'address', 'GPS_Lon', 'GPS_Lat', 'city_id'),
                'City' => array(
                    'fields' => array('id', 'name', 'url_name'),
                ),
            ),
            'Venue' => array(
                'fields' => array('id', 'name', 'slug', 'address', 'GPS_Lon', 'GPS_Lat', 'city_id'),
                'City' => array(
                    'fields' => array('id', 'name', 'url_name')
                )
            ),
            'Schedule' => array(
                'fields' => array('id', 'name'),
                'Date' => array(
                    'fields' => array('start', 'end'),
                    'conditions' => array(
                        'Date.start >=' => $qOpts['start'],
                        'Date.start <=' => $qOpts['end'],
                    ),
                ),
            ),
            'EventType' => array(
                'fields' => array('id', 'name', 'slug'),
            ),
            'EventSubType' => array(
                'fields' => array('id', 'name', 'slug'),
            ),
            'EventSubSubType' => array(
                'fields' => array('id', 'name', 'slug'),
            ),
        ),
        'joins' => array(
            array(
                'table' => $this->Schedule->table,
                'alias' => 'Schedule',
                'type' => 'INNER',
                'foreignKey' => false,
                'conditions' => array(
                    'Schedule.event_id = Event.id',
                ),
            ),
            array(
                'table' => $this->Schedule->Date->table,
                'alias' => 'Date',
                'type' => 'INNER',
                'foreignKey' => false,
                'conditions' => array(
                    'Date.schedule_id = Schedule.id',
                ),
            ),
            array(
                'table' => $this->EventTypesEvent->table,
                'alias' => 'EventTypesEvents',
                'type' => 'INNER',
                'foreignKey' => false,
                'conditions' => array(
                    'EventTypesEvents.event_id = Event.id',
                ),
            ),
            array(
                'table' => $this->EventSubTypesEvent->table,
                //'table' => 'event_sub_types_events',
                'alias' => 'EventSubTypesEvents',
                'type' => 'INNER',
                'foreignKey' => false,
                'conditions' => array(
                    'EventSubTypesEvents.event_id = Event.id',
                ),
            ),
            array(
                'table' => $this->EventSubSubTypesEvent->table,
                'alias' => 'EventSubSubTypesEvents',
                'type' => 'INNER',
                'foreignKey' => false,
                'conditions' => array(
                    'EventSubSubTypesEvents.event_id = Event.id',
                ),
            ),
        ),
        'conditions' => $qOpts['conditions'],
        'limit' => $qOpts['limit'],
        'group' => 'Event.id'
    ));
    return $data;
}

Ответы [ 4 ]

3 голосов
/ 03 июля 2011

В такой ситуации я, как правило, не использую ассоциации Cake или Containable, а сам создаю соединения:

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

Это немного коротко, но в результате получается именно тот запрос, который я хочу.

UPDATE

Давайте разберем ваш код по частям и посмотрим, где мы могли бы его улучшить. Первая часть - подготовка к find. Я переписал ваш код, пытаясь сделать его короче, и вот что я придумал:

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

Обратите внимание, что я устранил большинство операций. Это потому, что вы можете передать массив как значение в conditions, и Cake сделает его оператором IN(...) в SQL-запросе. Например: 'Model.field' => array(1,2,3) генерирует 'Model.field IN (1,2,3)'. Это работает так же, как OR, но требует меньше кода. Таким образом, приведенный выше блок кода делает то же самое, что и ваш код, но он короче.

Теперь идет сложная часть, find сама.

Обычно я бы рекомендовал принудительные объединения в одиночку, без Containable и с 'recursive'=>false. Я считаю, что обычно - лучший способ справиться со сложными находками. При использовании ассоциаций и контейнеров Cake выполняет несколько запросов SQL к базе данных (по одному запросу на модель / таблицу), что, как правило, неэффективно. Кроме того, Containable не всегда возвращает ожидаемые результаты (как вы заметили, когда пытались это сделать).

Но , поскольку в вашем случае задействовано четыре сложных ассоциаций, возможно, смешанный подход будет идеальным решением - в противном случае было бы слишком сложно очистить дубликат данные. (4 сложные ассоциации: Событие hasMany Dates [через Событие hasMany Schedule, Расписание hasMany Date], Событие HABTM EventType, Событие HABTM EventSubType, Событие HABTM EventSubSubType). Таким образом, мы могли бы позволить Cake обрабатывать извлечение данных EventType, EventSubType и EventSubSubType, избегая слишком большого количества дубликатов.

Итак, вот что я предлагаю: используйте объединения для всех необходимых фильтров, но не включайте в поля типы Date и [Sub [Sub]]. Из-за имеющихся у вас ассоциаций моделей Cake автоматически выполнит дополнительные запросы к БД, чтобы получить эти биты данных. Не требуется контейнер.

код:

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

Мы исключили contains, и из-за этого были запущены некоторые дополнительные запросы Cake. Большинство соединений имеют тип INNER. Это означает, что по крайней мере одна запись должна существовать в обеих таблицах, участвующих в объединении, иначе вы получите меньше результатов, чем вы ожидаете. Я предполагаю, что каждое Событие происходит в Ресторане ИЛИ Место, но не в обоих, поэтому я использовал LEFT для этих таблиц (и городов). Если некоторые из полей, используемых в объединениях, являются необязательными, вы должны использовать LEFT вместо INNER в связанных объединениях.

Если бы мы использовали 'recursive'=>false здесь, мы все равно получили бы правильные события, без повторения данных, но даты и типы [Sub [Sub]] были бы пропущены. С двумя уровнями рекурсии Cake будет автоматически перебирать возвращаемые события и для каждого события будет запускать необходимые запросы для извлечения связанных данных модели.

Это почти то, что вы делали, но без Containable и с несколькими дополнительными настройками. Я знаю, что это все еще длинный, уродливый и скучный кусок кода, но в конце концов в нем задействовано 13 таблиц базы данных ...

Это весь непроверенный код, но я считаю, что он должен работать.

1 голос
/ 09 июля 2011

GROUP_CONCAT на помощь !!! Короче говоря - мне нужно было возвращать События с их многочисленными датами (с возможностью запроса по разным таблицам HABTM) - но когда я пытался, я либо получал слишком много событий (по одному на каждую дату ... и т. Д.) Или Я бы использовал GROUP BY и не получил бы все даты. Ответ ... по-прежнему использовать GROUP BY, но объединить даты в одном поле с помощью GROUP_CONCAT:

$qOpts['fields'] = array(
        ...
        'GROUP_CONCAT(Date.start, "|", Date.end ORDER BY Date.start ASC SEPARATOR "||") AS EventDates'
    );

Я публикую много кода - не стесняйтесь в браузер, если вы застряли, как я.

Вещи, которые я узнал:

  • НЕ рекомендуется использовать содержать И объединять - выберите один и придерживайтесь его - это было проклятием моего существования на некоторое время - я бы заставил что-то работать, но тогда не с / пагинацией ... и т. Д. И т. Д.
  • Если вам нужно выполнить запрос на основе данных HABTM, выберите объединение, не содержащее
  • Мои объединения работали просто отлично, но я получал одно и то же событие в 10 раз (по 1 на каждую существующую дату)
  • Но когда я попытался GROUP BY, он объединил их, поэтому я получил только 1 свидание, когда мне действительно нужны были все даты
  • GROUP_CONCAT потрясающе (никогда не слышал об этом раньше)

Надеюсь, это кому-нибудь поможет. Не стесняйтесь указывать на любые проблемы с моим кодом - я всегда хотел бы улучшить. Но сейчас я танцую в кругах, потому что это работает !!! Теперь, когда это работает, я вернусь и попытаюсь почистить те OR, как упомянуто @bfavaretto.

    //returns events based on category, subcategory, and start/end datetimes
function getEvents($opts = null) {
    //$opts = limit, start(date), end(date), types, subtypes, subsubtypes, cities, paginate(0,1), venues, excludes(event ids)

    $qOpts['conditions'] = array();

    //order
    $qOpts['order'] = 'Date.start ASC';
    if(isset($opts['order'])) $qOpts['order'] = $opts['order'];

    //dates
    $qOpts['start'] = date('Y-m-d') . ' 00:00:00';
    if(isset($opts['start'])) $qOpts['start'] = $opts['start'];

    //limit
    $qOpts['limit'] = 10;
    if(isset($opts['limit'])) $qOpts['limit'] = $opts['limit'];

    //event excludes (example: when you want "other events at this venue", you need to exclude current event)
    if(isset($opts['excludes'])) {
        if(is_array($opts['excludes'])) {
            foreach($opts['excludes'] as $exclude_id) {
                array_push($qOpts['conditions'], array('Event.id <>' => $exclude_id));
            }
        }
    }

    //approval status conditions
    if(!isset($opts['approval_statuses'])) $opts['approval_statuses'] = array('1'); //default 1 = approved
    if(isset($opts['approval_statuses'])) {
        if(is_array($opts['approval_statuses'])) {
            $approvalStatusesConditions['OR'] = array();
            foreach($opts['approval_statuses'] as $status) {
                array_push($approvalStatusesConditions['OR'], array('Event.approval_status_id' => $status));
            }
            array_push($qOpts['conditions'], $approvalStatusesConditions);
        }
    }

    //date conditions
    $date_conditions = array();
    array_push($qOpts['conditions'], array('Date.start >=' => $qOpts['start']));
    array_push($date_conditions, array('Date.start >=' => $qOpts['start']));

    if(isset($opts['end'])) {
        array_push($qOpts['conditions'], array('Date.start <=' => $opts['end']));
        array_push($date_conditions, array('Date.start <=' => $opts['end']));
    }


    //venues conditions
    if(isset($opts['venues'])) {
        if(is_array($opts['venues'])) {
            $venueConditions['OR'] = array();
            foreach($opts['venues'] as $venue_id) {
                array_push($venueConditions['OR'], array('OR'=>array('Venue.id'=>$venue_id)));
            }
            array_push($qOpts['conditions'], $venueConditions);
        }
    }

    //cities conditions
    if(isset($opts['cities'])) {
        if(is_array($opts['cities'])) {
            $cityConditions['OR'] = array();
            foreach($opts['cities'] as $city_id) {
                array_push($cityConditions['OR'], array('OR'=>array('Venue.city_id'=>$city_id, 'Restaurant.city_id'=>$city_id)));
            }
            array_push($qOpts['conditions'], $cityConditions);
        }
    }

    //event types conditions
    if(isset($opts['event_types'])) {
        if(is_array($opts['event_types'])) {
            $eventTypeConditions['OR'] = array();
            foreach($opts['event_types'] as $event_type_id) {
                array_push($eventTypeConditions['OR'], array('EventTypesEvents.event_type_id' => $event_type_id));
            }
            array_push($qOpts['conditions'], $eventTypeConditions);
        }
    }

    //event sub types conditions
    if(isset($opts['event_sub_types'])) {
        if(is_array($opts['event_sub_types'])) {
            $eventSubTypeConditions['OR'] = array();
            foreach($opts['event_sub_types'] as $event_sub_type_id) {
                array_push($eventSubTypeConditions['OR'], array('EventSubTypesEvents.event_sub_type_id' => $event_sub_type_id));
            }
            array_push($qOpts['conditions'], $eventSubTypeConditions);
        }
    }

    //event sub sub types conditions
    if(isset($opts['event_sub_sub_types'])) {
        if(is_array($opts['event_sub_sub_types'])) {
            $eventSubSubTypeConditions['OR'] = array();
            foreach($opts['event_sub_sub_types'] as $event_sub_sub_type_id) {
                array_push($eventSubSubTypeConditions['OR'], array('EventSubSubTypesEvents.event_sub_sub_type_id' => $event_sub_sub_type_id));
            }
            array_push($qOpts['conditions'], $eventSubSubTypeConditions);
        }
    }


    //joins
    $qOpts['joins'] = array();

    //Restaurants join
    array_push($qOpts['joins'], array(
            'table' => $this->Restaurant->table,
            'alias' => 'Restaurant',
            'type' => 'LEFT',
            'foreignKey' => false,
            'conditions' => array(
                'Restaurant.id = Event.restaurant_id',
            ),
        )
    );

    //Venues join
    array_push($qOpts['joins'], array(
            'table' => $this->Venue->table,
            'alias' => 'Venue',
            'type' => 'LEFT',
            'foreignKey' => false,
            'conditions' => array(
                'Venue.id = Event.venue_id',
            ),
        )
    );

    //Schedules join
    array_push($qOpts['joins'], array(
            'table' => $this->Schedule->table,
            'alias' => 'Schedule',
            'type' => 'INNER',
            'foreignKey' => false,
            'conditions' => array(
                'Schedule.event_id = Event.id',
            ),
        )
    );

    //Dates join
    array_push($qOpts['joins'], array(
        'table' => $this->Schedule->Date->table,
        'alias' => 'Date',
        'type' => 'INNER',
        'foreignKey' => false,
        'conditions' => array(
            'Date.schedule_id = Schedule.id',
            //$date_conditions
        ),
    ));

    //Uploads join
    array_push($qOpts['joins'], array(
            'table' => $this->Upload->table,
            'alias' => 'Upload',
            'type' => 'LEFT',
            'foreignKey' => false,
            'conditions' => array(
                'Upload.event_id = Event.id',
            ),
        )
    );

    //Event types join
    if(isset($opts['event_types'])) {
        if(is_array($opts['event_types'])) {
            array_push($qOpts['joins'], array(
                'table' => $this->EventTypesEvent->table,
                'alias' => 'EventTypesEvents',
                'type' => 'INNER',
                'foreignKey' => false,
                'conditions' => array(
                    'EventTypesEvents.event_id = Event.id',
                ),
            ));
        }
    }
    if(isset($opts['event_sub_types'])) {
        if(is_array($opts['event_sub_types'])) {
            array_push($qOpts['joins'], array(
                'table' => $this->EventSubTypesEvent->table,
                'alias' => 'EventSubTypesEvents',
                'type' => 'INNER',
                'foreignKey' => false,
                'conditions' => array(
                    'EventSubTypesEvents.event_id = Event.id',
                ),
            ));
        }
    }
    if(isset($opts['event_sub_sub_types'])) {
        if(is_array($opts['event_sub_sub_types'])) {
            array_push($qOpts['joins'], array(
                'table' => $this->EventSubSubTypesEvent->table,
                'alias' => 'EventSubSubTypesEvents',
                'type' => 'INNER',
                'foreignKey' => false,
                'conditions' => array(
                    'EventSubSubTypesEvents.event_id = Event.id',
                ),
            ));
        }
    }

    $qOpts['fields'] = array(
        'Event.*',
        'Venue.id', 'Venue.slug', 'Venue.name', 'Venue.GPS_Lon', 'Venue.GPS_Lat',
        'Restaurant.id', 'Restaurant.slug', 'Restaurant.name', 'Restaurant.GPS_Lat', 'Restaurant.GPS_Lon',
        'GROUP_CONCAT(Date.start, "|", Date.end ORDER BY Date.start ASC SEPARATOR "||") AS EventDates'
    );

    //group by
    $qOpts['group'] = 'Event.id';

    //you need to set the recursion to -1 for this type of join-search
    $this->recursive = -1;


    $paginate = false;
    if(isset($opts['paginate'])) {
        if($opts['paginate']) {
            $paginate = true;
        }
    }

    //either return the options just created (paginate)
    if($paginate) {
        return $qOpts;

    //or return the events data
    } else {
        $data = $this->find('all', $qOpts);
        return $data;
    }

}
1 голос
/ 16 июня 2011

Вы можете попробовать следующее, предполагая Cake 1.3 и используя сдерживаемое поведение. Я предположил, что поля даты в вашей таблице называются start_date и end_date, поэтому эти условия могут потребовать корректировки.

Это может работать, а может и не работать, и, скорее всего, приведет к массивному запросу, если у вас много данных, поэтому, возможно, потребуется дополнительное уточнение - конечно, с выбранными полями.

Вы можете опубликовать сгенерированный запрос, если он будет выполнен, и мы сможем помочь в его дальнейшей настройке.

/* in your EventController (the method) for on the fly */
$this->Event->Behaviors->attach('Containable');

/* Your dates */
$x_date = '2011-06-01';
$y_date = '2011-07-01';

$this->paginate = array(
            'limit'=>10,
            'order'=>'Event.created DESC',
            'contain'=>array(
                'Schedule',
                'Event'=>array(
                    'conditions'=>array('Event.start_date'=>$x_date,
                                        'Event.end_date'<=$y_date)
                )
            ),
        );

$this->set('events',$this->Paginate('Event'));

// print_r($events);
0 голосов
/ 08 июля 2011

Дейв, вам нужно решить достаточно сложную задачу, которая требует больше, чем просто Cake. Вы должны понимать, что происходит, чтобы иметь возможность решить это. Я предполагаю, что у вас нет большого опыта работы с SQL, и вы не знаете много «скрытого» торта. Поэтому я попытаюсь объяснить основы здесь.

Предположим, у вас есть две таблицы, которые называются 'main' и 'related':

main             related

id | val         id | main_id | val
1  | A           1  | 1       | Foo
2  | B           2  | 1       | FooBar
3  | C           3  | 2       | Bar
4  | D           4  | 3       | BarFoo

В Cake у вас будут модели Main и Related для работы с ними. Main hasMany Related и Related belongsTo Main. Теперь вы делаете следующее (из метода внутри Main):

$data = $this->find('all', array(
    'recursive' => 1
));

Вот что Cake будет делать за кулисами:

  1. Получить все строки из таблицы 'main'

    SELECT * FROM main
    
  2. Получив результаты, Cake создаст массив идентификаторов, которые затем будут использованы для получения данных для связанной модели Related. Эти данные будут получены из MySQL с помощью запроса, подобного следующему:

    SELECT * FROM related WHERE main_id IN ([comma_separated_list_of_ids_here])
    
  3. Наконец, Cake будет проходить через массив результатов из Main и добавлять соответствующие данные в каждую строку в зависимости от обстоятельств. Когда он заканчивается, он возвращает «украшенный» массив.

Иногда, в зависимости от типа ассоциации, Cake выполняет дополнительный SQL-запрос для каждой строки, извлекаемой для основной модели. Это может быть очень медленно. Решение состоит в том, чтобы использовать один запрос для получения данных из обеих таблиц, и это то, для чего нужны соединения. Проблема с этим является повторение данных. Например:

SELECT Main.*, Related.*
FROM main as Main
INNER JOIN related AS Related
ON Related.main_id = main.id

Результаты:

    Main.id | Main.val | Related.id | Related.main_id | Related.val
    1       | A        | 1          | 1               | Foo
    1       | A        | 2          | 1               | FooBar
    2       | B        | 3          | 2               | Bar
    3       | C        | 4          | 3               | BarFoo

На что стоит обратить внимание:

  1. У нас есть 2 строки для Main.id = 1. Разница между ними - в Related.id и Related.val. Если вы удалите эти столбцы из предложения SELECT, повтор исчезнет. Это очень полезно, если вам нужно добавить условия в соответствующую таблицу. Например:

    SELECT DISTINCT Main.*
    FROM main as Main
    INNER JOIN related AS Related
    ON Related.main_id = main.id
    WHERE Related.val LIKE '%Foo%'
    

    Дает:

   
        Main.id | Main.val
        1       | A       
        3       | C     

На самом деле есть две связанные строки, которые соответствуют нашим условиям (Foo и FooBar), но A появляется в результатах только один раз, потому что мы не просили SQL отображать Related.val, а также велел ему игнорировать точные дубликаты (с DISTINCT).

  1. В исходных результатах отсутствует пункт D из Main! Это потому, что мы использовали INNER JOIN, который ограничивает результаты строками из Main, которые также имеют одну или несколько соответствующих строк в Related. Если бы мы использовали LEFT JOIN, результаты имели бы дополнительную строку, как показано ниже:
    Main.id | Main.val | Related.id | Related.main_id | Related.val
    1       | A        | 1          | 1               | Foo
    1       | A        | 2          | 1               | FooBar
    2       | B        | 3          | 2               | Bar
    3       | C        | 4          | 3               | BarFoo
    4       | D        | NULL       | NULL            | NULL

(если вам нужна более подробная информация о ВНУТРЕННИХ и ЛЕВЫХ СОЕДИНЕНИЯХ, см. здесь ). ( РЕДАКТИРОВАТЬ : ссылка обновлена)

Возвращаясь к дубликатам: их легко очистить с помощью простого цикла foreach в PHP. Это просто, когда задействовано всего две таблицы, но становится все более и более сложным для каждой дополнительной таблицы, которую вы добавляете в запрос (если новая таблица имеет отношение один ко многим с основной или связанной).

Но у вас много таблиц и ассоциаций. Итак, решение, которое я предложил выше, является компромиссом между производительностью и простотой кода. Позвольте мне попытаться объяснить мою точку зрения, когда я ее написал.

  • Вам нужно разобраться с 13 таблицами, чтобы получить все нужные вам данные. Вам необходимо отобразить данные, поступающие из большинства этих таблиц, и отфильтровать события по нескольким таблицам.

  • Один Cake не может понять, что вы хотите, и вернет слишком много данных, включая то, что вы ожидали, что он отфильтровывался.

  • В игре участвуют 1-n и n-n перекомпоновки. Если вы просто добавите все 13 к одному запросу с JOIN, результат будет иметь слишком много дупликов и будет неуправляемым.

  • Поэтому я решил попробовать смешанный подход: начать с получения отфильтрованного списка событий без дублирования, а затем позволить Cake «украсить» его данными из некоторых связанных моделей. Для этого необходимо:

    1. JOIN все таблицы, для которых необходимы условия. Это позволит нам получить наш окончательный список событий с учетом всех условий одним запросом.
    2. Если какая-либо из таблиц, которые вы JOIN редактировали, может привести к дублированию, не включайте их поля в предложение SELECT (или список fields Cake). Если ассоциации установлены правильно, Cake позже запустит дополнительный запрос, чтобы получить связанные данные (так как мы использовали recursive=2).
    3. Запретить Cake выполнять дополнительные запросы для получения данных, которые мы уже получили по нашему основному запросу. Это делается путем открепления соответствующих моделей перед запуском поиска.

    Если по-прежнему возвращаются поля, которые вам не нужны, и такие поля получены из связанных моделей, вы должны использовать Containable, чтобы сообщить Cake, какие поля вы хотите получить от каждой из этих моделей.

Я знаю, что это может показаться сложным, но вы не сможете решить это самостоятельно, если не поймете, что делает Cake и как работает SQL. Надеюсь, это поможет.

...