CakePHP5中distinct()去重失效及beforeFind回调影响排查
CakePHP5中distinct()去重失效的原因及解决办法
问题场景
我需要在CakePHP5里对rubriques表的查询结果去重,表结构如下:
id | ... | model | ... ====================== 1 | ... | CustomRubriques | ... 2 | ... | CustomRubriques | ... 3 | ... | CustomRubriques | ... 4 | ... | CustomRubriques | ...
写了如下查询代码:
$rubriquesModels = $this->Rubriques ->find() ->select('model') ->distinct() ->all() ->extract('model'); debug($rubriquesModels->toArray());
但结果并没有去重,返回了四条重复的CustomRubriques:
(int) 0 => 'CustomRubriques', (int) 1 => 'CustomRubriques', (int) 2 => 'CustomRubriques', (int) 3 => 'CustomRubriques',
后来排查发现问题出在RubriquesTable.php的beforeFind回调中:
// in RubriquesTable.php public function beforeFind(Event $event, SelectQuery $query, ArrayObject $options, $primary) { $query ->enableAutoFields() ->find('translations'); return $query; }
删掉这个回调后,distinct()就能正常去重了,想知道为什么这个回调会导致去重失效?
原因分析
enableAutoFields()的干扰
这个方法会强制查询返回表中所有字段,包括id这种唯一标识字段。你原本只指定了查询model字段,但启用自动字段后,实际查询会把id等所有字段都包含进来。distinct()是对整行数据做去重——因为每条记录的id都不一样,所以整行数据都是唯一的,自然无法按model去重。find('translations')的影响
翻译查询会自动关联对应的翻译表(比如rubriques_translations),这会让查询结果带上翻译表的字段。哪怕主表的model字段值相同,不同的翻译记录(或者每条主记录关联的翻译条目)会让整条结果行变得唯一,进一步导致distinct()无法按预期去重。
解决办法
方法一:手动关闭自动字段
在查询中明确关闭自动字段,覆盖回调的设置:
$rubriquesModels = $this->Rubriques ->find() ->select('model') ->disableAutoFields() // 关闭自动字段,只返回指定的model字段 ->distinct() ->all() ->extract('model');
方法二:修改beforeFind回调,添加条件控制
给回调里的自动字段和翻译查询加上判断,只在需要的时候启用:
// in RubriquesTable.php public function beforeFind(Event $event, SelectQuery $query, ArrayObject $options, $primary) { // 只有当查询没指定select字段时,才启用自动字段 if (empty($query->clause('select'))) { $query->enableAutoFields(); } // 通过options参数控制是否启用翻译查询 if (!isset($options['skip_translations']) || !$options['skip_translations']) { $query->find('translations'); } return $query; }
然后在需要去重的查询里跳过翻译:
$rubriquesModels = $this->Rubriques ->find() ->select('model') ->distinct() ->setOption('skip_translations', true) // 跳过翻译查询 ->all() ->extract('model');
方法三:指定distinct的去重字段
明确告诉框架只按model字段去重,忽略其他字段的影响:
$rubriquesModels = $this->Rubriques ->find() ->select('model') ->distinct('model') // 指定仅按model字段去重 ->all() ->extract('model');
内容的提问来源于stack exchange,提问作者Oliv
相关产品推荐
相关产品推荐

