Laravel关联中间表查询:使用whereIn过滤后仍返回全部Status数据如何修正
问题描述
我有一个Status class,它和roles之间通过中间表建立了关联关系,关联定义如下:
public function roles(): { return $this->belongsToMany(Role::class, 'status_role', 'status_id', 'role_id'); }
Status数据库表结构如下:
| id | title |
|---|---|
| 1 | status1 |
| 2 | status2 |
| 3 | status3 |
对应的关联中间表结构如下:
| status_id | role_id |
|---|---|
| 1 | 2 |
| 2 | 2 |
现在我需要编写query查询所有关联role_id=2的status记录,预期返回结果为status1、status2,不包含status3。
我目前的实现代码如下:
$statuses = Status::query() ->leftJoin('status_role', function ($join) { $join->on('statuses.id', '=', 'status_role.status_id') ->whereIn('status_role.role_id',[2]); }) ->get();
但当前写法会返回全部3条status记录,不符合需求,请问应该如何修改查询语句?
解决方案
你当前写法返回全部记录的核心原因是使用了leftJoin左连接:左连接会保留左表(statuses表)的所有记录,即使右表没有匹配项也会返回,因此无关联的status3也会被查询出来。
方案1:使用Eloquent关联查询whereHas(推荐)
你已经定义了roles关联,直接使用whereHas方法即可,无需手动编写join逻辑,代码更简洁易维护:
$statuses = Status::whereHas('roles', function ($query) { $query->where('role_id', 2); })->get();
whereHas的作用就是仅返回满足关联条件的模型记录,完全匹配你的需求。
方案2:修改join类型为内连接
如果你需要保留手动写join的写法,把leftJoin改为join(Laravel中join默认是内连接),内连接仅返回两表都有匹配的记录,额外加distinct避免同一status关联多个符合条件的role时返回重复数据:
$statuses = Status::query() ->join('status_role', function ($join) { $join->on('statuses.id', '=', 'status_role.status_id') ->where('status_role.role_id', 2); }) ->distinct() ->get();
内容的提问来源于stack exchange,提问作者zyyyzzz
相关产品推荐
相关产品推荐

