如何在Yii2的HasMany关联中添加指定CASE WHEN条件?
解决方案
要在Yii2的HasMany关联ON条件中加入你需要的CASE WHEN逻辑,有两种常用实现方式,可根据是否需要全局生效选择:
方式一:修改全局关联定义(永久生效)
如果所有用到TaskGoal的tasks关联的场景都需要该条件,直接修改模型里的关联方法:
// 在TaskGoal模型中 use yii\db\Expression; public function getTasks() { return $this->hasMany(Task::class, ['goal_id' => 'id']) ->onCondition(new Expression( 'CASE WHEN {{%task}}.type = :type_personal AND {{%task}}.user_author_id != {{%user}}.id THEN {{%user}}.show_personal_task = 0 ELSE TRUE END', [':type_personal' => 'personal'] )); }
说明:
{{%task}}和{{%user}}是Yii2的表前缀占位符,会自动替换为你的实际表前缀,避免硬编码表名onCondition()方法会把条件注入到LEFT JOIN的ON子句中,而非WHERE子句,完全匹配你的需求
方式二:在特定查询中动态添加条件(仅当前查询生效)
如果只想在某一次查询中应用该条件,不想修改全局关联,可在joinWith中使用回调:
// 业务逻辑中的查询代码 use yii\db\Expression; $query = TaskGoal::find() // 确保user表已关联,根据你的实际关联关系调整,比如TaskGoal有user关联则加:->joinWith('user') ->joinWith(['tasks' => function($taskQuery) { $taskQuery->onCondition(new Expression( 'CASE WHEN {{%task}}.type = :type_personal AND {{%task}}.user_author_id != {{%user}}.id THEN {{%user}}.show_personal_task = 0 ELSE TRUE END', [':type_personal' => 'personal'] )); }]) // 其他查询条件... ->all();
可选优化:用AND/OR替代CASE WHEN
你的CASE WHEN逻辑可以等价转换为更简洁的AND/OR组合,性能和效果一致,可读性可能更好:
// 替换Expression中的内容为: new Expression( '({{%task}}.type != :type_personal OR {{%task}}.user_author_id = {{%user}}.id OR {{%user}}.show_personal_task = 0)', [':type_personal' => 'personal'] )
注意事项
- 确保
user表已被加入当前查询(比如通过joinWith('user')或直接join),否则会报表不存在的错误 - 如果你的关联使用了自定义别名,需要把表达式中的
{{%task}}/{{%user}}替换为实际别名
内容的提问来源于stack exchange,提问作者Mexalim
相关产品推荐
相关产品推荐

