【问题标题】:Select All Events with Event->Schedule->Date between start and end dates in CakePHP在 CakePHP 中使用 Event->Schedule->Date between start and end dates 选择所有事件
【发布时间】:2011-09-16 10:09:03
【问题描述】:

试图弄清楚如何在 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 始终是相同的 Date - 它们还包括一个时间(两个日期时间字段) - 因此我要检查两者的“开始”。


我尝试过使用可包含,我已经尝试过 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;
}

【问题讨论】:

  • Dave,我正在查看您的更新。当然有一些方法可以避免使用 Containable AND 手动连接,并且仍然可以获得您想要的结果。我会调查一下,然后再回复你。但我对你所有的 OR 条件有点困惑,我不确定你想从数据库中获取什么数据。
  • 另外:餐厅、场地和活动之间的关系是什么?
  • @bfavaretto - 求任何帮助 - 我快疯了。 “OR”允许用户选择他们想要将结果缩小到的列表事件类型。即——我只想看“体育”、“电影”和“户外”——所以我用“或”来表示eventType 必须是这些类型之一。与subTypes 和@ 相同987654331@...等Event belongsTo Venue Event belongsTo Restaurant(基本上,这两个地点都可以举行活动 - 只是在不同的桌子上)
  • 好的@Dave,我已经进行了一些测试,现在我明白了发生了什么。现在我将编辑我的原始答案以获取详细信息。
  • @bfavaretto - 老实说,我迫不及待想看看你想出了什么。

标签: mysql cakephp associations cakephp-1.3


【解决方案1】:

假设 Cake 1.3 并使用可包含的行为,您可以尝试以下内容。我假设您表中的日期字段称为start_dateend_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);

【讨论】:

  • 这听起来不错,但您根本没有提到 Dates 表 - 它是要检查实际日期的表。想法?
  • 根据您的回答更新了我的问题/我尝试了什么......仍然没有运气:(
  • 该代码示例中的条件无效。它们不是数组。应该是:array('Event.start_date &gt;=' =&gt; $x_date, 'Event.end_date &lt;=' =&gt; $y_date)... CakepPHP 将解析出应该是 in 键(字符串)的操作,=&gt; 是一个数组键/值关联运算符。
  • @Andrew - 很好 - 但现在我遇到的问题是,无论条件中的日期如何,它都会拉动事件..想法?条件是现在 - 如果日期在正确的范围内,它只会显示日期日期 - 但无论如何都会提取事件的数据。
【解决方案2】:

在这种情况下,我倾向于不使用 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
));

它有点笨重,但会产生我想要的确切查询。

更新

让我们将您的代码分成几部分,看看我们可以改进它的地方。第一部分是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']
}

请注意,我消除了大部分 OR。这是因为您可以将数组作为值传递给conditions,并且Cake 会在SQL 查询中将其设为IN(...) 语句。例如:'Model.field' =&gt; array(1,2,3) 生成 'Model.field IN (1,2,3)'。这就像 OR 一样工作,但需要的代码更少。所以上面的代码块和你的代码完全一样,但是更短。

现在是复杂的部分,find 本身。

通常我建议单独使用强制连接,不使用 Containable,使用 'recursive'=&gt;false。我相信这通常是处理复杂发现的最佳方式。使用 Associations 和 Containable,Cake 对数据库运行多个 SQL 查询(每个模型/表一个查询),这往往是低效的。此外, Containable 并不总是返回预期的结果(正如您在尝试时注意到的那样)。

但是因为在您的情况下涉及四个复杂的关联,也许混合方法将是理想的解决方案 - 否则,清理起来太复杂了重复的数据。 (这 4 个复杂的关联是:Event hasMany Dates [通过 Event hasMany Schedule, Schedule hasMany Date]、Event HABTM EventType、Event HABTM EventSubType、Event HABTM EventSubSubType)。所以,我们可以让 Cake 处理 EventType、EventSubType 和 EventSubSubType 的数据检索,避免过多的重复。

所以我的建议是:对所有必需的过滤使用连接,但不要在字段中包含日期和 [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'=&gt;false,我们仍然会得到正确的事件,并且没有数据重复,但是日期和[Sub[Sub]]类型会丢失。使用 2 级递归,Cake 将自动循环返回的事件,并为每个事件运行必要的查询以获取关联的模型数据。

这几乎就是您所做的,但没有 Containable,并且有一些额外的调整。我知道这仍然是一段又长又丑又无聊的代码,但毕竟涉及到 13 个数据库表......

这都是未经测试的代码,但我相信它应该可以工作。

【讨论】:

  • 我理解日期上外键=>false 的原因,但是 Schedule 是否有必要,因为它们是相关的?谢谢,我试试看!
  • @Dave,bfavaretto 正在设置 foreignKey =&gt; false,因此他可以显式编码 JOIN condition。当您没有直接的 JOIN 或遵循 CakePHP 约定的 JOIN 时,这很有帮助。但是,在您的情况下,您可以适当地使用foreignKey
  • @Dave,我记得一件事:上面的查询将返回所有这 3 个模型的所有字段。如果您不需要全部,则应将“字段”选项添加到$this-&gt;Event-&gt;find(包含您需要的字段的数组)。
  • @bfavaretto - 谢谢。获得太多字段将是一个很大的问题。我已经实现了 - 现在正在测试。
  • @bfavaretto - 它只返回一个日期记录。在我尝试的其中一个中,它返回了 3 个不同的事件时间表,每个都有日期,但它只返回一个日期记录。
【解决方案3】:

Dave,你有一个相当复杂的问题要解决,这需要的不仅仅是基本的 Cake。您必须了解正在发生的事情才能解决它。我假设您对 SQL 没有太多经验,并且不太了解“幕后”蛋糕。因此,我将尝试在这里解释基础知识。

假设您有两个表,分别称为“主”和“相关”:

主要相关 编号 |有效身份证件 | main_id |值 1 | 1 | 1 |富 2 |乙 2 | 1 |富吧 3 | C 3 | 2 |酒吧 4 | 4 | 3 |吧台

在 Cake 中,您将拥有 Main 和 Related 模型来处理它们。 Main hasMany RelatedRelated belongsTo Main。现在您执行以下操作(从 Main 中的方法):

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

以下是 Cake 在幕后所做的:

  1. 从表'main'中检索所有行

    SELECT * FROM main
    
  2. 根据结果,Cake 将构建一个 ID 数组,然后用于获取关联模型相关的数据。这些数据将使用如下查询从 MySQL 中获取:

    SELECT * FROM related WHERE main_id IN ([comma_separated_list_of_ids_here])
    
  3. 最后,Cake 将遍历 Main 中的 results 数组,并根据需要将相关数据添加到每一行。完成后,它返回“装饰”数组。

有时,根据关联的类型,Cake 会对为主模型检索到的每一行执行额外的 SQL 查询。那可能真的很慢。解决方案是使用单个查询从两个表中获取数据,这就是 JOIN 的用途。问题在于数据重复。例如:

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

结果:

Main.id |主.val |相关.id |相关.main_id |相关.val 1 |一个 | 1 | 1 |富 1 |一个 | 2 | 1 |富吧 2 |乙| 3 | 2 |酒吧 3 | C | 4 | 3 |吧台

注意事项:

  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 |主值 1 |一种 3 | C

related 上实际上有 2 行匹配我们的条件(Foo 和 FooBar),但 A 在结果中只显示一次,因为我们没有要求 SQL 显示 Related.val,并且还告诉它忽略确切重复(DISTINCT)。

  1. 在原始结果中,缺少 Main 中的 D 项!那是因为我们使用了INNER JOIN,它将结果限制为来自 Main 的行,这些行在相关上也有一个或多个相应的行。如果我们使用LEFT JOIN,结果会多出一行,如下所示:
Main.id |主.val |相关.id |相关.main_id |相关.val 1 |一个 | 1 | 1 |富 1 |一个 | 2 | 1 |富吧 2 |乙| 3 | 2 |酒吧 3 | C | 4 | 3 |吧台 4 | D |空 |空 |空值

(如果您需要有关 INNER 与 LEFT JOIN 的更多详细信息,请参阅 here)。 (编辑:链接已更新)

回到重复项:使用 PHP 中的简单 foreach 循环很容易清理它们。当只涉及 2 个表时,这很简单,但对于您添加到查询中的每个额外表,它会变得越来越复杂(如果新表与主表或相关表具有一对多关系)。

但您确实有很多表和关联。所以,我上面建议的解决方案在某种程度上是性能和代码简单性之间的折衷。让我试着解释一下我写这篇文章时的思路。

  • 你需要处理13个表才能得到你想要的所有数据。您需要显示来自大多数这些表的数据,并且还需要根据相当多的表过滤事件。

  • Cake 本身无法理解您想要什么,并且会返回太多数据,包括您希望它过滤掉的内容。

  • 涉及一些 1-n 和 n-n relashionships。如果您只是将所有 13 个添加到带有 JOIN 的单个查询中,则结果将包含太多重复项,并且将无法管理。

  • 所以我决定尝试一种混合方法:首先获取过滤后的事件列表,没有欺骗,然后让 Cake 用来自一些相关模型的数据“装饰”它。为此,您必须:

    1. JOIN 所有需要对其应用条件的表。这将使我们能够在考虑所有条件的情况下,通过一个查询来检索我们的最终事件列表。
    2. 如果您JOINed 的任何表可能导致重复,请不要在SELECT 子句(或Cake 的fields 列表)中包含它们的字段。如果关联设置正确,Cake 稍后会运行额外的查询来获取关联数据(因为我们使用了recursive=2)。
    3. 防止 Cake 运行额外的查询来获取我们已经在主查询中检索到的数据。这是通过在运行查找之前取消绑定正确的模型来完成的。

    如果这仍然返回您不想要的字段,并且这些字段来自关联的模型,您必须使用 Containable 告诉 Cake 您希望从每个模型中获得哪些字段。

我知道这听起来可能很复杂,但是除非您了解 Cake 的功能以及 SQL 的工作原理,否则您将无法自己解决这个问题。我希望这会有所帮助。

【讨论】:

    【解决方案4】:

    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数据查询,选择join,不包含
    • 我的加入工作正常,但我会收到 10 次相同的事件(每个存在的日期 1 次)
    • 但是当我尝试 GROUP BY 时,它将它们组合在一起,所以我只有 1 个日期,而我确实需要所有日期
    • GROUP_CONCAT 很棒(以前从未听说过)

    希望这对某人有所帮助。随时指出我的代码的任何问题 - 我总是喜欢改进。但是现在,我在转圈跳舞,因为它有效!!!现在它可以工作了,我要回去尝试清理@bfavaretto 提到的那些 OR。

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

    【讨论】:

      猜你喜欢
      • 2020-07-21
      • 2022-12-02
      • 2021-10-07
      • 1970-01-01
      • 2022-12-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-09-02
      相关资源
      最近更新 更多