如何在 CakePhp 索引中添加相关记录的计数

How do I add a count of related records in CakePhp index

我正在开发一个为青年团体存储记录的应用程序。

这个青年团体有一个徽章系统供成员实现,我有以下 tables;

成员 徽章 member_badges

前两个table是不言自明的,members是青年团成员,badges是徽章名称,例如。 FieldCraft, Life Saving, First Aid 等等

member_badges 记录了成员的成就,并将有 member_id、badge_id、尝试日期和状态(通过或失败)。我们记录失败的尝试,因为这将帮助我们衡量课程的有效性 - 通过率是多少等等。

我想用以下数据创建一个 table;

徽章 |尝试次数 |通过 |失败

我创建了一个 badgeController 函数,如下所示;

public function badgereportsummary() {

    $paginate = array(
        'contain' => array('MemberBadge'));

    $this->Paginator->settings = $paginate;
    $this->set('badges', $this->paginate());
}

现在这以一种方式工作 - 它 returns 一个徽章数组,每个徽章包含一个会员徽章数组,我有一个粗略的现成页面,可以在视图中使用以下内容

echo h(count($badge['MemberBadge']));

但是 - 这不允许我在这个字段上排序,而且将来我想添加限制日期范围的功能,例如"Show me the count of First Aid badges attempted from 1/1/15 through 30/3/15"。我在网上看到了一些关于使用 counterCache 的建议(我在应用程序的其他地方使用过它)但这不允许我计算日期范围等的计数。

我想我想要的是将特定页面上的会员徽章计数添加为控制器中的虚拟字段。我一直在搜索文档和 Google,整天在键盘上敲打我的头,但无法解决。非常感谢有关如何实现这一目标的任何建议。

您的总结报告(徽章 | 尝试次数 | 通过 | Fail) 看起来它需要一个 SQL 语句,例如:

SELECT badge.description AS Badge
  , COUNT(*) AS NumberOfAttempts
  , SUM(IF(member_badges.status = 'Fail',0,1)) AS Fails 
  , SUM(IF(member_badges.status = 'Pass',0,1)) AS Passes 
FROM member_badges
JOIN badges ON badges.id = member_badges.badge_id
WHERE member_badges.date_of_attempt BETWEEN '20140101' AND '20140131'
GROUP BY badge.description
ORDER BY NumberOfAttempts DESC;

然后我会更改您的 badgereportsummary 方法来生成此 SQL,这相对简单。

  1. 将功能从 Badge 控制器模型移动到 MemberBadge 模型
  2. 在方法中为新列名称创建虚拟字段 ->
    1. $this->MemberBadge->virtualFields['NumberOfAttempts'] = 0;
    2. $this->MemberBadge->virtualFields['Fails'] = 0;
    3. $this->MemberBadge->virtualFields['Passes'] = 0;
  3. 写下查找:

代码示例

$fields = array('Badge.description'
                ,'COUNT(*) AS MemberBadge__NumberOfAttempts'
                ,'SUM(IF(MemberBadge.status = 'Fail',0,1)) AS MemberBadge__Fails' 
                ,'SUM(IF(MemberBadge.status = 'Pass',0,1)) AS MemberBadge__Passes');
return $this->find('all'
       ,'conditions' => array('MemberBadge.date_of_attempt BETWEEN ? AND ?' => array($start_date,$end_date)
       ,'fields' => $fields
       ,'order' => array('NumberOfAttempts DESC')
       ,'group' => array('Badge.description'));

然后在任何 Badge 控制器方法中调用 $this->Badge->MemberBadge-> badgereportsummary($start_date, $end_date)

我不会使用 permanent 虚拟字段(即在模型中定义虚拟字段),因为这是一个聚合函数。另外,出于您的确切原因,我不会使用 counterCache - 需要根据另一个字段过滤结果。