按计数关联数据过滤查询 CakePHP 3

Filter query by count associated data CakePHP 3

我想查询“文章”,但我只想要有两个或更多“评论”的“文章”。所以我必须对“评论”进行计数,并在 where 子句中使用计数结果。

我知道下一个代码是错误的,但应该是这样的:

$articles = $this->Articles->find();

$articles->matching('Comments', function ($q) {
    $commentsCount = $q->func()->count('Comments.id');   
    return $q->where($commentsCount . ' >= ' => 2);
});

我找不到关于此的任何信息。

首先弄清楚如何在原始 SQL 中执行它总是有帮助的,这将使您更容易弄清楚如何使用 CakePHP 的查询构建器复制它。

例如,您不能在 WHERE 子句中使用聚合,这在所有支持的 DBMS 中都是禁止的。 WHERE 应用 分组之前被评估(即在可以计算任何东西之前),你必须改为检查 HAVING 子句中的聚合,这是在 分组后评估

通常你会按照 SQL:

SELECT
    Articles.id, ...
FROM
    articles Articles
LEFT JOIN
    comments Comments ON Comments.article_id = Articles.id
GROUP BY
    Articles.id
HAVING
    COUNT(Comments.id) >= 2

这可以通过这样的查询构建器轻松实现:

$query = $this->Articles
    ->find()
    ->leftJoinWith('Comments')
    ->group('Articles.id')
    ->having(function (
        \Cake\Database\Expression\QueryExpression $exp,
        \Cake\ORM\Query $query
    ) {
        return $exp->gte(
            $query->func()->count('Comments.id'),
            2,
            'integer'
        );
    });

这样的查询在大型 table 上可能非常昂贵,因为它不能使用任何索引,因此需要完整的 table 扫描,即它必须检查每一行。避免这种情况的一种方法是使用计数器缓存,它将关联记录的数量存储在源 table 中,这样您就可以与 Articles table 中的可索引列进行比较,即然后可以简单地做:

$query = $this->Articles
    ->find()
    ->where([
        'Articles.comment_count >=' => 2,
    ]);

另见