使用 Containable 按关联模型 (HABTM) 上的条件过滤的分页结果 [英] Paginate results filtered by condition on associated model (HABTM) using Containable

查看:39
本文介绍了使用 Containable 按关联模型 (HABTM) 上的条件过滤的分页结果的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I need to paginate list of Products belonging to specific Category (HABTM association).

在我的 Product 模型中,我有

In my Product model I have

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 看起来像这样:

However, resulting SQL looks like this:

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)

(I.e. it selects 20 Products and then queries their 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;

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

(I.e. select top 20 Products which belong to Category with id = 3)

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

Note: Possible solution without Containable would be (as Dave suggested) using joins. This post offers a very handy helper to build $this->paginate['joins'] to paginate over HABTM association.

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

Note: Still looking for more elegant solution using Containable than fake hasOne binding.

推荐答案

我终于找到了做我想做的事情的方法,因此将其发布为答案:

Finally I found a way to do what I want, so posting it as an answer:

Containable 中强制 JOIN(并能够通过关联模型的条件过滤) - 你必须使用假的hasOne关联.

To force JOIN (and be able to filter via condition on associated model) in Containable - you've got to use fake hasOne association.

就我而言,ProductsController 中的代码应该是:

In my case, code in ProductsController should be:

$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.

Note false as a second argument to bindModel - which makes binding persistent. This is needed because paginate() issues find('count') before find('all'), which would reset temporary binding. So you might want to manually unbindModel afterwards.

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

Also, if your condition includes multiple IDs in HABTM associated model, you might want to add 'group' => 'Product.id' into your $this->paginate[] (as Aziz has shown in his answer) to eliminate duplicate entries (will work on MySQL only).

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

UPDATE: However, this approach has one serious drawback compared to joins approach (suggested by Dave): condition can apply only to intermediate model's foreign key (category_id in my case); if you want to use condition on any other field in associated model - you'd probably have to add another bindModel('hasOne'), binding intermediate model to HABTM associated model.

这篇关于使用 Containable 按关联模型 (HABTM) 上的条件过滤的分页结果的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆