2

我需要对Product属于特定Category(HABTM 关联)的 s 列表进行分页。

在我的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

(即它选择 20Products然后查询它们的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属于Categorywith id= 3的前20名)

注意: 可能的解决方案Containable是(如Dave建议的)使用连接。 这篇文章提供了一个非常方便的助手来构建$this->paginate['joins']对 HABTM 关联进行分页。

注意:仍然在寻找Containable比假hasOne绑定更优雅的解决方案。

4

2 回答 2

6

最后,我找到了一种方法来做我想做的事,因此将其发布为答案:

要强制JOIN(并能够通过关联模型上的条件进行过滤)Containable-你必须使用假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('count')find('all')unbindModel

此外,如果您的条件在 HABTM 关联模型中包含多个 ID,您可能希望添加'group' => 'Product.id'到您的$this->paginate[](如Aziz在他的回答中所示)以消除重复条目(仅适用于 MySQL)。

更新: 但是,与连接方法(Dave建议)相比,这种方法有一个严重的缺点:条件只能适用于中间模型的外键(category_id在我的情况下);如果您想在关联模型中的任何其他字段上使用条件 - 您可能必须添加另一个bindModel('hasOne')绑定中间模型到 HABTM 关联模型。

于 2011-07-10T18:11:20.840 回答
1

当您将条件放入嵌套的 Contain 中时,您要求它仅检索具有该 ID 的类别。所以 - 它正在做你所要求的,但这不是你想要的。

虽然看起来应该是可能的,但我唯一的运气是通过 Joins 而不是 Contain做你想做的事情(经过许多小时和几个 stackoverflow 问题)。

http://book.cakephp.org/view/1047/Joining-tables

这不是完全相同的问题,但您可以查看我的一些代码,我在此处查询 HABTM 条件(我在底部回答了我的问题):使用 Event->Schedule->Date between start and end dates in 选择所有事件CakePHP

于 2011-07-10T05:20:52.467 回答