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());
}
}
你可以使用query
方法,但该方法被视为最后的手段,当find
或任何其他方便的方法是不够的。
但也可以在Cake find
方法see the cookbook中手动指定连接。我不完全确定,但我相信也支持嵌套连接。 CONCAT_WS
只能在find方法的field
属性中与相关字段一起调用。
CakePHP 2.0具有虚拟字段,您可以在其中指定任何SQL作为该字段,包括子查询和CONCAT_WS – gustavotkg
如果您想使用“虚拟字段”,则它们也存在于Cake 1.3中:http://book.cakephp。 org/view/1608/Virtual-fields – mensch
当然,你不能使用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',
)
))
我希望它对你有用
“Model :: query()'方法是否是最好的方法? – freshest
我这么认为,因为这种方式可以缓存,并且返回的结果与$ this-> find()相近。 – gustavotkg
你可以使用子查询来做到这一点,这会不会更好的代码维护? – freshest