CakePHP 3 - 计算多对多关联

CakePHP 3 - Count many-to-many Association

我有一个包含 Rounds 和 Users 的数据库。 Rounds belongsToMany Users 和 Users belongsToMany Rounds,因此是多对多关系。为此添加了连接 table rounds_users。 编辑:此处使用了错误的短语。我的意思是 'belongsToMany' 而不是 'hasMany'

现在我想检索回合列表,以及每回合链接的用户数。

在一对多关系的情况下,像下面这样的东西会起作用:

$rounds = $this->Rounds->find()
       ->contain(['Users' => function ($q) 
        {
            return $q->select(['Users.id', 'number' => 'COUNT(Users.round_id)'])
                    ->group(['Users.round_id']);
        }
        ]);

...根据

但是,在多对多关系中 Users.round_id 不存在。那么,什么可以代替呢?

注:这样的问题已经问了好几个了,但是关于Cake的很少PHP3.x,所以我还是想试试这个。

注意 2:我可以通过使用 PHP 函数 count 来解决这个问题,尽管我宁愿做得更优雅

编辑:如下所示,手动加入似乎可以解决问题:

$rounds_new = $this->Rounds->find()
        ->select($this->Rounds)
        ->select(['user_count' => 'COUNT(Rounds.id)'])
        ->leftJoinWith('Users')
        ->group('Rounds.id');

...有一个问题!没有用户的回合仍然得到等于 1 的 user_count。可能是什么问题?

加入协会

正如评论中已经提到的,您始终可以加入协会而不是包含它们(有点像您的 )。

您为没有任何关联用户的回合检索计数 1 的原因是您计算错误 table。计数 Rounds 当然会导致计数至少 1,因为总会有至少 1 轮,即没有关联用户存在的轮。

长话短说,请指望 Users

$rounds = $this->Rounds
    ->find()
    ->select($this->Rounds)
    ->select(function (\Cake\ORM\Query $query) {
        return [
            'user_count' => $query->func()->count('Users.id')
        ];
    })
    ->leftJoinWith('Users')
    ->group('Rounds.id');

belongsToMany 个关联的计数器缓存

还有计数器缓存行为,它也适用于 belongsToMany 关联,只要它们被设置为使用具体的连接 table class,可以配置通过关联配置 through 选项:

class RoundsTable extends Table
{
    public function initialize(array $config)
    {
        $this->belongsToMany('Users', [
            'through' => 'RoundsUsers'
        ]);
    }
}

class UsersTable extends Table
{
    public function initialize(array $config)
    {
        $this->belongsToMany('Rounds', [
            'through' => 'RoundsUsers'
        ]);
    }
}

计数器缓存将在连接 table class 中设置,在此示例中 RoundsUsersTable,它将有两个 belongsTo 关联,一个到 Rounds 和一到 Users:

class RoundsUsersTable extends Table
{
    public function initialize(array $config)
    {
        $this->belongsTo('Rounds');
        $this->belongsTo('Users');

        $this->addBehavior('CounterCache', [
            'Rounds' => ['user_count']
        ]);
    }
}

遏制计数

如果您实际上明确创建了 hasMany 关联(而不是 belongsToMany),那么您将拥有 one Rounds to many RoundsUsers 关系,即您仍然可以使用链接的“”示例来自 RoundsUsers.

但是,这会给您留下一个结构,在该结构中,计数将放置在嵌套实体中,而对于没有任何关联用户的轮次,该实体又会丢失。我想在大多数情况下,这种结构需要重新格式化,所以它可能不是最好的解决方案。然而,为了完整起见,这里有一个例子:

$rounds = $this->Rounds
    ->find()
    ->contain(['RoundsUsers' => function (\Cake\ORM\Query $query) {
        return $query
            ->select(function (\Cake\ORM\Query $query) {
                return [
                    'RoundsUsers.round_id',
                    'number' => $query->func()->count('RoundsUsers.round_id')
                ];
            })
            ->group(['RoundsUsers.round_id']);
        }]
    );

另见