CakePHP - 构建一个复杂的查询

问题描述:

我有我想要使用CakePHP构建的以下查询。我应该怎么做呢?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`; 

你应该把它放在你的模型中。如果你还没有阅读,我建议你阅读关于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()); 
    } 
} 
+1

“Model :: query()'方法是否是最好的方法? – freshest

+0

我这么认为,因为这种方式可以缓存,并且返回的结果与$ this-> find()相近。 – gustavotkg

+1

你可以使用子查询来做到这一点,这会不会更好的代码维护? – freshest

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

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

+1

CakePHP 2.0具有虚拟字段,您可以在其中指定任何SQL作为该字段,包括子查询和CONCAT_WS – gustavotkg

+0

如果您想使用“虚拟字段”,则它们也存在于Cake 1.3中:http://book.cakephp。 org/view/1608/Virtual-fields – mensch

+0

当然,你不能使用virtualFields为每个Artist获取'Portfolio.count'? – freshest

不要尝试使用ORM构建该查询。这将是一场慢动作的灾难。

相反,您应该尝试尽可能做到“接近金属”。我不熟悉CakePHP的API(因为我倾向于避免它像黑色瘟疫),但你应该能够创建一个与ActiveRecord无关的Model,并且最有可能访问与PDO包装类似的任何东西。用它来执行查询并将结果映射到变量。


这就是说,你可能要检查你的查询(你似乎有一些强迫报价病)。你可以做的一个改进就是停止在表格中使用id列。

这整个设置可以真正受益于创建艺术家表中的另一列: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' 

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

我认为,该模型是艺术家和它有一个属于关联关系,那么你可以在这条路上使用CakePHP的ORM和的hasMany与投资组合

首先你每亩distroy艺术家和投资组合

之间的关系

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

,然后建立关系

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

最后,你必须创建其他relationsships

$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', 
) 
)) 

我希望它对你有用