Yii2中andFilterWhere适配with()失效,joinWith()正常的问题求助
问题分析
首先得掰扯清楚joinWith()和with()的核心差异:
joinWith()会真正执行SQL JOIN操作,把关联表(这里是company)和主表(employee)合并到同一个查询里,所以你能直接在WHERE/HAVING里用关联表的字段,这也是你第一种写法能正常筛选的原因。with()只是做预加载(或者延迟加载):它先查主表的所有数据,再单独发一条SQL批量拉取关联的company数据,主查询本身根本没和company表关联,这就是为啥你用company.name会报“字段不存在”——主查询的FROM里只有employee表啊!
解决方案
如果你既想筛选关联表字段,又想预加载关联数据(避免N+1查询),有两种靠谱的实现方式:
方式1:用joinWith()(推荐,最直接)
其实joinWith()默认已经会自动预加载关联的company数据了,不用额外加with()。你原来的代码本身就没问题,既能正常筛选,又能拿到预加载的关联数据,直接用就行:
$query = Employee::find()->joinWith(['company']); $dataProvider = new ActiveDataProvider([ 'query' => $query, 'pagination' => false, 'sort' => false, ]); if (!($this->load($params) && $this->validate())) { return $dataProvider; } // 这里筛选有效,因为已经JOIN了company表 $query->andFilterWhere(['like', 'company.name', $this->company_id]);
要是担心预加载的问题,可以明确指定joinWith的第二个参数为true(不过默认就是true):
$query->joinWith(['company'], true);
方式2:用with()配合exists子查询(满足你的需求,但不推荐)
如果你非要用with()来预加载,同时还要筛选关联表字段,那得用EXISTS子查询绕一下,让主查询通过子判断当前员工对应的公司是否符合条件:
$query = Employee::find()->with(['company']); $dataProvider = new ActiveDataProvider([ 'query' => $query, 'pagination' => false, 'sort' => false, ]); if (!($this->load($params) && $this->validate())) { return $dataProvider; } // 用exists子查询关联company的筛选条件 $query->andWhere(['exists', Company::find() ->select('1') ->where(['id' => new \yii\db\Expression('employee.company_id')]) ->andFilterWhere(['like', 'name', $this->company_id]) ]);
这个逻辑是:只保留那些存在对应公司记录,且公司名称符合筛选条件的员工数据。主查询还是只查employee表,但通过子查询实现了关联筛选,同时with()会预加载符合条件的公司数据。
总结
- 要是需要基于关联表字段做筛选,优先用
joinWith(),这是最符合SQL逻辑的写法,代码也简洁。 with()的核心作用是预加载关联数据避免N+1问题,不是用来做关联筛选的,强行用就得绕圈子写子查询。
内容的提问来源于stack exchange,提问作者always-a-learner
相关产品推荐
相关产品推荐

