使用Containable通过相关模型(HABTM)上的条件过滤分页结果

lxa*_*lxa 2 cakephp has-and-belongs-to-many paginate containable cakephp-1.3

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

在我的Product模型中,我有

var $actsAs = array('Containable');
var $hasAndBelongsToMany = array(
    'Category' => array(
        'joinTable' => 'products_categories'
    )
);
Run Code Online (Sandbox Code Playgroud)

并在 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());
Run Code Online (Sandbox Code Playgroud)

但是,生成的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
Run Code Online (Sandbox Code Playgroud)

(即它选择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;
Run Code Online (Sandbox Code Playgroud)

(即选择前20 Products属于Categoryid= 3)

注意: 没有可能的解决方案Containable(正如Dave建议的那样)使用连接. 这篇文章提供了一个非常方便的帮助器,$this->paginate['joins']可以通过HABTM关联构建分页.

注意:仍然在寻找Containable比使用假hasOne装订更优雅的解决方案.

lxa*_*lxa 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());
Run Code Online (Sandbox Code Playgroud)

注意false作为第二个参数bindModel- 这使得绑定持久化.这是必要的,因为之前的paginate()问题会重置临时绑定.所以你可能想要手动之后.find('count')find('all')unbindModel

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

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