CakePHP3:查询HasMany关联匹配或无关联的用户记录
解决CakePHP中查询未参加测试或测试过期用户的问题
嘿,我来帮你搞定这个需求!你的现有代码已经能筛选出参加过创建时间超过6个月测试的用户,但还缺了完全没参加任何测试的用户这部分。下面给你两种可行的方案:
方案一:使用左连接(Left Join)结合OR条件
这个方法通过leftJoinWith保留所有符合status=1的用户,再通过条件筛选出“无测试”或“测试过期”的用户,最后分组去重避免重复结果:
$users = $this->Users->find('all') ->where(['Users.status' => 1]) // 左连接Tests表,关联条件是测试创建时间超过6个月 ->leftJoinWith('Tests', function($q) { return $q->where(['Tests.created <' => new \DateTime('-6 months')]); }) // 按用户ID分组,避免一个用户因多个测试重复出现 ->group(['Users.id']) ->having([ 'OR' => [ // 没有关联任何测试(即未参加过测试) 'COUNT(Tests.id) = 0', // 确保用户所有关联测试都超过6个月(若需求是只要有一个符合,可简化逻辑) 'MAX(Tests.created) <' => new \DateTime('-6 months') ] ]);
关键说明:
leftJoinWith和matching的核心区别:matching是内连接,只会返回有匹配测试的用户;而leftJoinWith是左连接,会保留所有用户,哪怕没有关联测试。COUNT(Tests.id) = 0:直接判断该用户没有任何测试记录。
方案二:使用Union合并两个查询
这个方法逻辑更直观:分别查询“无测试的用户”和“有过期测试的用户”,再合并结果:
// 查询有至少一个过期测试的用户 $usersWithOldTests = $this->Users->find() ->where(['Users.status' => 1]) ->matching('Tests', function($q) { return $q->where(['Tests.created <' => new \DateTime('-6 months')]); }); // 查询从未参加任何测试的用户 $usersWithoutTests = $this->Users->find() ->where(['Users.status' => 1]) ->notMatching('Tests'); // 合并两个查询结果,自动去重 $users = $usersWithOldTests->union($usersWithoutTests);
关键说明:
notMatching('Tests')是CakePHP提供的便捷方法,直接返回没有任何关联测试的用户。union()会自动合并两个查询的结果并去重,确保每个用户只出现一次。
两种方案都能满足你的需求,你可以根据业务场景和性能要求选择~
内容的提问来源于stack exchange,提问作者Kristis
相关产品推荐
相关产品推荐

