CakePHP - 构建一个复杂的查询

Posted

技术标签:

【中文标题】CakePHP - 构建一个复杂的查询【英文标题】:CakePHP - Building a Complex Query 【发布时间】:2011-11-23 11:48:21 【问题描述】:

我想使用 Cakephp 构建以下查询。我该怎么办?

        SELECT
            `Artist`.`id`,
            CONCAT_WS(' ', `Person`.`first_name`, `Person`.`last_name`, `Person`.`post_nominal_letters`) AS `name`,
            `Portfolio`.`count`
        FROM
            `people` as `Person`,
            `artists` as `Artist`
        LEFT OUTER JOIN
            (SELECT
                `Product`.`artist_id`,
                 COUNT(DISTINCT `Product`.`id`) AS `count`
            FROM
                `product_availabilities` AS `ProductAvailability`,
                `products` AS `Product`
            LEFT OUTER JOIN
                `order_details` AS `OrderDetail`
            ON
                `Product`.`id` = `OrderDetail`.`product_id`
            LEFT OUTER JOIN
                `orders` AS `Order`
            ON
                `Order`.`id` = `OrderDetail`.`order_id`
            WHERE
                `ProductAvailability`.`id` = `Product`.`product_availability_id`
            AND
                `Product`.`online` = true
            AND
                (`ProductAvailability`.`name` = 'For sale')
                OR
                    ((`ProductAvailability`.`name` = 'Sold') AND (DATEDIFF(now(),`Order`.`order_date`) <= 30))
            GROUP BY
                `Product`.`artist_id`)
        AS
            `Portfolio`
        ON
            `Artist`.`id` = `Portfolio`.`artist_id`
        WHERE
            `Artist`.`person_id` = `Person`.`id`
        AND
            `Artist`.`online` = true
        GROUP BY
            `Artist`.`id`
        ORDER BY
            `Person`.`last_name`, `Person`.`first_name`;

【问题讨论】:

【参考方案1】:

我认为模型是 Artist 并且它与 有一个 belongsTo 关系,那么你可以用这种方式使用 CakePHP ORM 和 hasMany 和 Portfolio

首先你必须破坏艺术家和作品集之间的关系

$this-&gt;Artist-&gt;unbindModel(array('hasMany'=&gt;array('Portfolio')));

然后建立关系

$this->Artist->bindModel(array('hasOne'=>array('Portfolio')));

最后你必须创建其他关系

$this->Artist->bindModel(array(
'belongsTo'=>array(
'Product'=>array(
 'clasName'=>'Product',
 'foreignKey'=> false,
 'conditions'=>'Product.id = Artist.product_id'
 ),
 'ProductAvaibility'=>array(
 'clasName'=>'ProductAvaibility',
 'foreignKey'=> false,
 'conditions'=>'ProductAvaibility.id = Product.product_avaibility_id'
 ),
 'OrderDetail'=>array(
 'clasName'=>'OrderDetail',
 'foreignKey'=> false,
 'conditions'=>'Product.id = OrderDetail.product_id'
 ),
 'Order'=>array(
 'clasName'=>'Order',
 'foreignKey'=> false,
 'conditions'=>'Order.id = OrderDetail.order_id'
 ),
)
));

现在,当关系完成后,你可以做你的发现

$this->Artist->find('all', array(
'conditions'=>array(
'Artist.online'=>true
),
'group'=>array(
'Artist.id'
),
'order'=>array(
'Person.last_name', 
'Person.first_name', 
)
))

希望对你有用

【讨论】:

【参考方案2】:

你应该把它放在你的模型中。如果你还没有阅读,我建议你阅读skinny controllers and fat models。

class YourModel extends AppModel 

    public function getArtistsAndPortfolioCounts() 
        return $this->query("SELECT ... ");
    


所以在你的控制器中:

class ArtistsControllre extends AppController 
    public function yourAction() 
        debug($this->YourModel->getArtistsAndPortfolioCounts());
    

【讨论】:

Model::query() 方法是最好的方法吗? 我想是的,因为这样你可以缓存并且它返回的结果与 $this->find() 一样接近。 能不能用子查询来做,这样对代码维护不是更好吗?【参考方案3】:

不要尝试使用 ORM 构建该查询。慢镜头会是一场灾难。

相反,您应该尽可能“接近金属”地执行该请求。我不熟悉 CakePHP 的 API(因为我倾向于像黑死病一样避免它),但是您应该能够创建一个与 ActiveRecord 无关的Model,并且很可能可以访问任何类似于 PDO 包装器的东西。使用它来执行查询并将结果映射到变量。


也就是说,您可能想要检查您的查询(您似乎有一些强迫性的引用病)。您可以做的一项改进是停止在表中使用id 列。

整个设置可以真正受益于在 Artists 表中创建另一列:portfolio_size。每次应该更改时都可以更新。是的,它会使你的表去规范化,但它也会使这个查询变得微不足道和快速,在其他地方花费很少的成本。


对于列的名称,如果表Artists有一个id,则保留在列artist_id中,如果Products有一个外键引用Artists.artist_id,那么也将其命名为artist_id。相同的东西应该在你的数据库中具有相同的名称。这还可以让您在 SQL USING 语句中使用。这是一个小例子:

SELECT 
   Users.name
   Users.email
FROM Users
LEFT JOIN GroupUsers USING (user_id)
LEFT JOIN Groups USING (group_id)
WHERE Groups.name = 'wheel'

我假设这个查询不需要解释,因为它是用户和组之间简单的多对多关系。

【讨论】:

【参考方案4】:

您可以使用query 方法,但是当find 或任何其他便捷方法都不够用时,该方法被视为最后的手段。

但也可以在 Cake find 方法 see the cookbook 中手动指定连接。我不完全确定,但我相信嵌套连接也受支持。 CONCAT_WS 可以通过相关字段在 find 方法的 field 属性中调用。

【讨论】:

CakePHP 2.0 有虚拟字段,您可以在其中指定任何 SQL 作为该字段,包括子查询和 CONCAT_WS 如果你想使用“虚拟字段”,它们也出现在 Cake 1.3 中:book.cakephp.org/view/1608/Virtual-fields 你肯定不能使用 virtualFields 来获取每个 Artist 的 Portfolio.count 吗? 确实存在限制和可能的解决方法:book.cakephp.org/view/1608/… 据我所知,您无法指定使用子查询的连接,如问题中我的 SQL 语句所示...

以上是关于CakePHP - 构建一个复杂的查询的主要内容,如果未能解决你的问题,请参考以下文章

没有任何数据库表的CakePHP模型或控制器

cakephp 复杂查询

使用连接语句和子查询准备 CakePHP 查询

在 Zend Framework 中使用 Zend_Db_Table 的 CakePHP 风格的数据库查询结果?

将“包含”限制为第一个(CakePHP 3)

cakePHP 从自定义数组构建一个选择框