【问题标题】:CakePHP 3 - Count many-to-many AssociationCakePHP 3 - 计数多对多关联
【发布时间】:2018-10-10 04:50:13
【问题描述】:

我有一个包含 Rounds 和 Users 的数据库。 Rounds belongsToMany Users 和 Users belongsToMany Rounds,所以是多对多的关系。添加了一个连接表 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']);
        }
        ]);

...根据Count in contain Cakephp 3

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

注意:类似这样的问题已经有人问过了,但是关于 CakePHP 3.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。可能是什么问题?

【问题讨论】:

  • 你总是可以加入关联而不是包含它们(有点像你的 SUM() on ManyToMany question)...除此之外,如果你明确地创建了hasMany 关联(而不是belongsToMany),那么你有一个one Rounds to many RoundsUsers 关系,即你仍然可以通过RoundsUsers 使用“包含计数”示例,不是吗?
  • @ndm 哎呀,我用错了词组。真的是多对多。手动加入似乎是要走的路,我添加了一个例子。这确实导致了一个新问题。
  • 尝试使用 innerJoinWith 而不是 leftJoinWith !
  • @ManoharKhadka COUNT 通常不计算 NULL 值,即它应该与 LEFT 连接一起工作,这实际上可能是 OP 想要的,即检索所有轮次,并且没有用户的回合计数0。如果那些0 用户轮次是不需要的,那么我建议INNER 也加入。
  • 话虽如此,你不想指望Users吗?

标签: cakephp count many-to-many cakephp-3.0 query-builder


【解决方案1】:

加入协会

正如 cmets 中已经提到的,您始终可以加入关联而不是包含它们(有点像您的 SUM() on ManyToMany question)。

您在没有任何关联用户的回合中检索到1 的计数的原因是您指望了错误的表。依靠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 关联,只要它们设置为使用具体的连接表类,可以通过关联配置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'
        ]);
    }
}

计数器缓存将在连接表类中设置,在此示例中为 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 关系,即您仍然可以使用链接的“count in contain”示例通过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']);
        }]
    );

另见

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-08-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-18
    • 2015-06-01
    相关资源
    最近更新 更多