CakePHP 模型:在 Containable 中使用 COUNT(*)

12

我有一个CakePHP 1.3应用程序,并且非常喜欢使用Containable behavior获取数据。

假设我有一对多关系的帖子和评论。我使用Containable查询(用于分页)所有帖子和相关评论的列表。但我只关心每个帖子有多少评论。我没有找到使用Containable实现此查询的方法,而又不必获取所有评论行。我尝试过:

$this->paginate=array(
            'fields' => 'Post.title, Post.created',
            'contain' => array('Comment'=>'COUNT(*) AS count'),
        );

导致错误消息为“模型“ Comment”未与模型“ Count”关联”。

$this->paginate=array(
            'fields' => array('Post.title, Post.created'),
            'contain' => array('Comment'=>array('fields'=>'COUNT(*) AS count'),
        );

代码无效,结果集中对于每个 Post 都包含一个空的 Comment 数组,除了最后一个,它包含计数字段,但该字段包含所有评论数量而不仅仅是属于该 Post 的。

我猜测的另一种可能是

$this->paginate=array(
            'fields' => 'Post.title, Post.created, COUNT(Comment.id)',
            'contain' => array('Comment'=>array('fields'=>''),
        );

但是这样会导致错误,因为hasMany关系是单独查询的,所以答案表不在帖子条目的查询中。我该如何计算一篇文章有多少评论?


你是否找到了不需要 counterCache 的解决方案? - Miguel Stevens
2个回答

11

实现这个功能的最佳方法是在posts表中设置一个名为comment_count的字段,并将该键添加到Comment模型$belongsTo Post数组中:

'counterCache' => true

每当有关评论发生任何事情时,相关帖子中的comment_count字段都将被更新(实际上,每次都会重新计数,而不仅仅是添加或删除)。

这种方法更好,因为当您获取用户的数据时,速度更快,甚至不会涉及评论表。由于您正在使用Containable行为,我想速度和轻量级就是您要寻找的。


4
这是一个不错的想法,但它不能解决更复杂的查询问题,因为在这种情况下,与行相关联的计数无法起作用(例如用户选择某些条件的报告系统)。是否没有其他方法可以使用Containable获得计数? - Zxaos

5

我有同样的问题。PawelMysior提供的答案很好,带来了解决方案。

在CakePHP手册中我找到了更多的细节: 3.7.4.1.1 counterCache - 缓存计数器。这对我很有用,但还是有点模糊。为了其他人着想,我想提供一些进一步的细节。

我有两个表: Post和Image。当我向文章添加图片时,我想跟踪每篇文章有多少张图片,以显示在文章索引列表中,而不需要运行额外的计数查询在图像表上。

CREATE TABLE posts
(
id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
user_id INTEGER UNSIGNED NOT NULL,
title VARCHAR(255),
body TEXT,
created DATETIME,
modified DATETIME,
image_count INTEGER UNSIGNED,
PRIMARY KEY (id)
) ENGINE=InnoDB;

CREATE TABLE images
(
id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
post_id INTEGER UNSIGNED NOT NULL,
filename VARCHAR(255),
created DATETIME,
modified DATETIME,
PRIMARY KEY (id)
) ENGINE=InnoDB;

请注意,image_count 存储在 posts 表中。在 images 表中不需要任何特殊处理。
class Post extends AppModel
{
    var $name = 'Memory';
    var $hasMany = array(
        'Image' => array(
            'className' => 'Image',
            'foreignKey' => 'post_id',
            'dependent' => false
        )
    );
}

class Image extends AppModel
{
    var $name = 'Image';
    var $belongsTo = array(
        'Post' => array(
            'className' => 'Post',
            'foreignKey' => 'post_id',
            'counterCache' => true
        )
    );
}

请注意,counterCache 已添加到 Image 模型中。在 Post 模型中不需要进行任何特殊操作。
$this->Post->Behaviors->attach('Containable');
$post = $this->Post->find('all', array(
    'conditions' => array('user_id' => 7),
    'order' => array('Post.created DESC'),
    'contain' => array(
        'Post' => array(
            'User' => array('first_name', 'last_name')
        )
    )
));

现在当我们进行find操作时,不需要做任何特殊处理来查找计数,因为它已经自动成为Post结果中的一个字段。请注意下面的image_count
Array
(
[Post] => Array
        (
            [0] => Array
                (
                    [id] => 14
                    [user_id] => 7
                    [title] => Another great post
                    [body] => Lorem ipsum...
                    [created] => 2011-10-26 11:45:05
                    [modified] => 2011-10-26 11:45:05
                    [image_count] => 21
                    [User] => Array
                    (
                        [first_name] => John
                        [last_name] => Doe
                    )
                )
        )
)

需要注意的一点是,如果你将 counterCache 添加到现有的数据库结构中,在添加另一个 Image 之前,Post 中的计数将为零。此时,CakePHP 将进行实际计数并将 image_count 更新为正确的数量。

希望这个额外的信息对某人有帮助。


我注意到有人给这个帖子点了踩,但没有留下任何评论说明原因。也许这与这个问题/答案已经8年历史有关?无论如何,没有评论的踩是不礼貌的行为。 - SunSparc

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接