试图找出如何在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日期之间至少有一个Date条目的任何事件。
我还需要能够使用事件数据显示日期。
修改(修订):
我试过这个,但它似乎是在检查事件而不考虑日期,但只检索日期信息,如果日期在范围内:
$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总是相同的日期 - 它们还包括一个时间(两个日期时间字段) - 因此我为什么要检查两者的“开始”。
我尝试使用可包含的,我尝试过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;
}
答案 0 :(得分:3)
在这种情况下,我倾向于不使用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' => array(1,2,3)
生成'Model.field IN (1,2,3)'
。这就像OR一样,但需要的代码更少。因此,上面的代码块与您的代码完全相同,但它更短。
现在出现了复杂的部分,find
本身。
通常我会建议单独使用强制连接,不使用Containable,并使用'recursive'=>false
。我相信这个通常是处理复杂发现的最佳方式。使用Associations和Containable,Cake会针对数据库运行多个SQL查询(每个Model /表一个查询),这往往效率低下。此外,Containable并不总是返回预期的结果(正如您在尝试时所注意到的那样)。
但是因为在你的情况下涉及四个复杂关联,也许混合方法将是理想的解决方案 - 否则,清理起来会太复杂重复的数据。 (4个复杂的关联是:事件有很多日期[通过事件有多个时间表,时间表有多个日期],事件HABTM事件类型,事件HABTM事件类型,事件HABTM事件子类型子事件)。因此,我们可以让Cake处理EventType,EventSubType和EventSubSubType的数据检索,避免重复过多。
所以这就是我的建议:使用连接进行所有必需的过滤,但不要在字段中包含Date和[Sub [Sub]]类型。由于您具有模型关联,Cake将自动对DB运行额外查询以获取这些数据位。不需要包含。
代码:
// 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
,并且因为它而运行了一些额外的查询。大多数联接都是INNER
类型。这意味着在连接中涉及的两个表上必须至少存在一条记录,否则您将得到的结果会比您预期的要少。我假设每个活动都在一个餐厅或一个地点进行,但不是两个,这就是为什么我使用LEFT
表示这些表(和城市)的原因。如果联接中使用的某些字段是可选的,则应在相关联接上使用LEFT
而不是INNER
。
如果我们在这里使用'recursive'=>false
,我们仍然会得到正确的事件,并且没有数据重复,但是日期和[Sub [Sub]]类型将会丢失。通过2级递归,Cake将自动遍历返回的事件,并且对于每个事件,它将运行必要的查询以获取关联的模型数据。
这几乎就是你在做什么,但是没有包含,并且有一些额外的调整。我知道它仍然是一段冗长,丑陋和无聊的代码,但毕竟有13个数据库表...
这是所有未经测试的代码,但我相信它应该可行。
答案 1 :(得分:1)
您可以尝试以下内容,假设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);
答案 2 :(得分:1)
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'
);
我发布了很多代码 - 如果你像我一样陷入困境,请随意浏览。
我学到的东西:
希望这有助于某人。随意指出我的代码有任何问题 - 我总是希望改进。但是现在,我在圈子里跳舞,因为它起作用!现在它起作用了,我将回去并尝试清理像@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;
}
}
答案 3 :(得分:0)
考虑你有两个表,叫做'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将在幕后做的事情:
检索表'main'
中的所有行SELECT * FROM main
根据结果,Cake将构建一个ID数组,然后将用于获取相关模型的数据。这些数据将使用如下查询从MySQL获取:
SELECT * FROM related WHERE main_id IN ([comma_separated_list_of_ids_here])
最后,Cake将从Main循环遍历结果数组,并根据需要将相关数据添加到每一行。完成后,它将返回“装饰”数组。
有时,根据关联类型,Cake会为为主模型检索的每一行执行额外的SQL查询。那可能真的很慢。解决方案是使用单个查询从两个表中获取数据,这就是JOIN的用途。问题在于数据重复。例如:
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
注意事项:
我们有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
)。
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
(如果您需要有关INNER与LEFT JOIN的更多详细信息,请参阅here)。 (编辑:链接已更新)
回到重复项:使用PHP中的简单foreach
循环很容易清理它们。只涉及2个表时很简单,但是对于添加到查询中的每个额外表变得越来越复杂(如果新表与主表或相关表有一对多的关系)。
但你确实有很多表和协会参与其中。因此,我上面提出的解决方案在性能和代码简单性之间有所妥协。当我写这篇文章时,让我试着解释一下我的思路。
您需要处理13个表以获取所需的所有数据。您需要显示来自大多数表的数据,并且还需要基于相当多的表来过滤事件。
单独的蛋糕无法理解您的需求,并且会返回太多数据,包括您希望过滤掉的内容。
涉及一些1-n和n-n关系。如果您使用JOIN将所有13添加到单个查询中,结果将会有太多的欺骗,并且将无法管理。
所以我决定尝试一种混合的方法:首先获得一个过滤的事件列表,没有任何欺骗,然后让Cake用来自一些相关模型的数据“装饰”它。要做到这一点,你必须:
JOIN
所有需要条件的表格。这将允许我们通过单个查询检索我们的最终事件列表,考虑所有条件。JOIN
编辑的任何表格可能导致重复,请不要在SELECT
子句(或Cake的fields
列表)中包含其字段。如果正确设置了关联,Cake将在稍后运行额外的查询以获取关联数据(因为我们使用了recursive=2
)。如果这仍然是您不想要的返回字段,并且此类字段来自关联模型,则必须使用Containable告诉Cake您希望从每个模型中获取哪些字段。
我知道这可能听起来很复杂,但除非你了解Cake的功能以及SQL的工作原理,否则你无法自己解决这个问题。我希望这会有所帮助。