我需要对属于特定Product
(HABTM关联)的Category
的列表进行分页。
在我的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;
(即选择属于Products
的前20名Category
id
= 3)
注意:
没有Containable
的可能解决方案是使用连接(如 Dave 建议的那样)。
This帖子提供了一个非常方便的助手来构建$this->paginate['joins']
以对HABTM关联进行分页。
注意:仍在使用Containable
寻找比假hasOne
绑定更优雅的解决方案。
答案 0 :(得分:6)
最后,我找到了一种方法来做我想做的事情,所以将其作为答案发布:
在JOIN
- you've got to use fake hasOne
association中强制Containable
(并且能够通过相关模型上的条件进行过滤)。
就我而言,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('count')
之前发出find('all')
,这将重置临时绑定。因此,您可能希望之后手动unbindModel
。
此外,如果您的条件在HABTM关联模型中包含多个ID,您可能希望将'group' => 'Product.id'
添加到$this->paginate[]
(正如 Aziz 已在其答案中显示)消除重复的条目(仅适用于MySQL)。
<强>更新强>
但是,与连接方法(由 Dave 建议)相比,这种方法有一个严重的缺点:条件只能应用于中间模型的外键(在我的情况下为category_id
);如果要在关联模型中的任何其他字段上使用条件 - 您可能必须将另一个bindModel('hasOne')
绑定中间模型添加到HABTM关联模型。
答案 1 :(得分:1)
当您将条件放入嵌套的Contain中时,您要求它仅检索具有该ID的类别。所以 - 它正在做你所要求的,但那不是你想要的。
虽然看起来应该是可能的,我唯一的运气就是你做了什么(经过多个小时和一些stackoverflow问题)是通过联接而不是包含。
http://book.cakephp.org/view/1047/Joining-tables
这不是完全相同的问题,但你可以查看我的一些代码,我在这里查询HABTM条件(我在底部回答了我的问题):Select All Events with Event->Schedule->Date between start and end dates in CakePHP