我需要对属于特定Category
(HABTM关联(的Product
的列表进行分页。
在我的Product
型号中,我有
var $actsAs = array('Containable');
var $hasAndBelongsToMany = array(
'Category' => array(
'joinTable' => 'products_categories'
)
);
并且在ProductsController
中
$this->paginate = array(
'limit' => 20,
'order' => array('Product.name' => 'ASC'),
'contain' => array(
'Category' => array(
'conditions' => array(
'Category.id' => 3
)
)
)
);
$this->set('products', $this->paginate());
然而,结果SQL看起来是这样的:
SELECT COUNT(*) AS `count`
FROM `products` AS `Product`
WHERE 1 = 1;
SELECT `Product`.`*`
FROM `products` AS `Product`
WHERE 1 = 1
ORDER BY `Product`.`name` ASC
LIMIT 20;
SELECT `Category`.`*`, `ProductsCategory`.`category_id`, `ProductsCategory`.`product_id`
FROM `categories` AS `Category`
JOIN `products_categories` AS `ProductsCategory` ON (`ProductsCategory`.`product_id` IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20) AND `ProductsCategory`.`category_id` = `Category`.`id`)
WHERE `Category`.`id` = 3
(即,它选择20个Products
,然后查询它们的Categories
(
而我需要
SELECT COUNT(*) AS `count`
FROM `products` AS `Product`
JOIN `products_categories` AS `ProductsCategory` ON `ProductsCategory`.`product_id` = `Product`.`id`
JOIN `categories` AS `Category` ON `Category`.`id` = `ProductsCategory`.`category_id`
WHERE `Category`.`id` = 3;
SELECT `Product`.*, `Category`.*
FROM `products` AS `Product`
JOIN `products_categories` AS `ProductsCategory` ON `ProductsCategory`.`product_id` = `Product`.`id`
JOIN `categories` AS `Category` ON `Category`.`id` = `ProductsCategory`.`category_id`
WHERE `Category`.`id` = 3
ORDER BY `Product`.`name` ASC
LIMIT 20;
(即选择前20个属于Category
的Products
,id
=3(
注意:没有Containable
的可能解决方案是(正如Dave建议的那样(使用联接。这篇文章提供了一个非常方便的助手来构建$this->paginate['joins']
,以便在HABTM关联上进行分页。
注意:仍在寻找使用Containable
而不是伪造hasOne
绑定的更优雅的解决方案
最后我找到了一种做我想做的事的方法,所以把它作为答案发布:
要在Containable
中强制JOIN
(并能够通过关联模型上的条件进行过滤(,必须使用伪hasOne
关联。
在我的情况下,ProductsController
中的代码应该是:
$this->Product->bindModel(array('hasOne' => array('ProductsCategory')), false);
$this->paginate = array(
'limit' => 20,
'order' => array('Product.name' => 'ASC'),
'conditions' => array(
'ProductsCategory.category_id' => $category
),
'contain' => 'ProductsCategory'
);
$this->set('products', $this->paginate());
注意false
是bindModel
的第二个参数,它使绑定持久化。这是必要的,因为paginate()
在find('all')
之前发布find('count')
,这将重置临时绑定。因此,您可能需要在之后手动unbindModel
。
此外,如果您的条件在HABTM关联模型中包含多个ID,您可能需要将'group' => 'Product.id'
添加到$this->paginate[]
中(正如Aziz在他的回答中所示(,以消除重复条目(仅适用于MySQL(。
更新:然而,与联接方法(由Dave建议(相比,这种方法有一个严重的缺点:条件只能应用于中间模型的外键(在我的情况下为category_id
(;如果您想在关联模型中的任何其他字段上使用条件,您可能需要添加另一个bindModel('hasOne')
,将中间模型绑定到HABTM关联模型。
当您将条件放入嵌套的Contain中时,您要求它只检索具有该ID的Categories。因此,它正在执行您的要求,但这不是您想要的。
虽然这似乎是可能的,但我唯一的幸运是通过Joins而不是Contain来做你想做的事情(经过许多小时和几个堆叠的问题(。
http://book.cakephp.org/view/1047/Joining-tables
这不是完全相同的问题,但你可以在这里浏览我的一些代码,我在其中查询HABTM条件(我在底部回答了我的问题(:选择带有事件的所有事件->日程安排->CakePHP 中开始和结束日期之间的日期