Cakephp 分页按计算字段排序( COUNT )

Posted

技术标签:

【中文标题】Cakephp 分页按计算字段排序( COUNT )【英文标题】:Cakephp pagination sort by calculated field ( COUNT ) 【发布时间】:2011-08-26 11:16:36 【问题描述】:

在 cakephp 1.3 中使用 COUNT() 等计算字段时,我对分页列表进行排序时遇到问题

假设我有两个模型:文章和评论(1 篇文章 x N cmets),我想显示文章的分页列表,包括每个文章的 cmets 数。我会有这样的东西:

控制器:

$this->paginate = array('limit'=>80,
                        'recursive'=>-1,
                        'fields'=>array("Article.*","COUNT(Comment.id) as nbr_comments"),
                        'joins'=>array(array(   'table' => 'comments',
                                                    'alias' => 'Comment',
                                                    'type' => 'LEFT',
                                                    'conditions' => array('Comment.article_id = Article.id'))
                                            ),
                        'group'=>"Article.id"
                        );

(我必须覆盖 findCount() 方法才能使用 group by 进行分页)

问题是在视图中,sort() 方法不起作用:

<th><?php echo $this->Paginator->sort('nbr_comments');?></th> //life is not that easy

我能够通过“欺骗”分页和排序来创建解决方法:

控制器

$order = "Article.title";
$direction = "asc";
if(isset($this->passedArgs['sort']) && $this->passedArgs['sort']=="nbr_comments")
    $order = $this->passedArgs['sort'];
    $direction = $this->passedArgs['direction'];
    unset($this->passedArgs['sort']);
    unset($this->passedArgs['direction']);

$this->paginate = array(... 'order'=>$order." ".$direction, ...);
$this->set('articles', $this->paginate());
if($order == "clicks")
    $this->passedArgs['sort'] = $order;
    $this->passedArgs['direction'] = $direction;

查看

<?php $direction = (isset($this->passedArgs['direction']) && isset($this->passedArgs['sort']) && $this->passedArgs['sort'] == "nbr_comments" && $this->passedArgs['direction'] == "desc")?"asc":"desc";?>
<th><?php echo $this->Paginator->sort('Hits','clicks',array('direction'=>$direction));?></th>

而且它有效.. 但是对于一些应该对开发者透明的东西来说,似乎代码太多了。 (感觉就像我在做蛋糕的工作)所以我问是否有另一种更简单的方法。也许 cake 有这个功能,但决定隐藏它.. o_O.. 文档上没有关于这个的内容,我还没有在 S.O 上找到另一个好的解决方案......你是怎么做到的?

提前致谢!

【问题讨论】:

【参考方案1】:

也许您的问题可以使用Virtual fields 解决?

如果您为计算字段创建自定义字段,您将能够在该自定义字段上使用 Paginator->sort() 方法进行排序。

我在 cmets 中找到了解决方案there(不需要使用自定义字段来自定义分页方法等,而不是在 adnan 的初始解决方案中)。

【讨论】:

它当然有帮助,=) 使用虚拟字段我不必担心 $this-&gt;passedArgs,谢谢......但是,如果你想对结果进行分页,你仍然需要 override the paginateCount 方法跨度> 另一个问题是,有时您必须确定字段(条件 => 字段),因此不会请求/获取虚拟字段。因为否则,查询可能会改变。例如使用 virtualfield count(*) :find 方法将始终只返回 1 行。 不知道 vitualFields,现在我知道了,而且它们正是我需要的。谢谢! book.cakephp.org 的链接不再起作用。也许有人可以详细说明该链接下的代码?【参考方案2】:

您可能想要在模型定义中使用 Cake 的 counterCache 功能。不是在阅读时计算评论计数,而是在 articles 表中添加 nbr_comments 作为 int 字段。每次插入或删除Commentnbr_comments都会自动更新。

在您的Comment 模型中:

var $belongsTo = array(
    'Article' => array(
        'counterCache' => 'nbr_comments'
    )
);

现在您可以使用 $this-&gt;Paginator-&gt;sort('Article.nbr_comments'); 您无需在控制器中做任何时髦的事情。

【讨论】:

但是我担心如果我在 db 表中添加一个计算字段,Codd 的幽灵会在晚上骚扰我 XD...在这个例子中我只使用了一个我已经拥有的 COUNT在某些情况下,我在同一个查询中有一个 COUNT、一个 AVG 和一个公式。如果我将该信息存储在数据库中,我会冒数据完整性的风险【参考方案3】:

我对这个问题的解决方案如下:

将您的计算字段作为虚拟字段

$this->Comment->virtualFields = array(
    'nbr_comments' => 'COUNT(Comment.id)'
);

然后,您可以在分页顺序变量中使用该字段

$order = array("Comment.nbr_comments" => "DESC");
$this->Paginator->settings  = array("order" => $order);

【讨论】:

以上是关于Cakephp 分页按计算字段排序( COUNT )的主要内容,如果未能解决你的问题,请参考以下文章

CakePHP 3.0 中虚拟字段/实体属性上的分页排序链接

CakePHP 2.2.4 无法使用 HAVING 子句和计算字段对结果进行分页 - Haversine 公式

CakePHP 3:多订单导致分页?

CakePHP 分页:如何按多列排序以实现“粘性”功能?

cakephp 3.3:模型在url中时的分页和排序错误

Cakephp 2.x 在同一字段上查找同时具有 DISTINCT 和 COUNT 的查询