Laravel嵌套关联查询仅返回子级User表数据忽略父级数据
Laravel 嵌套关联查询仅返回最子级User数据方案
现有场景说明
涉及表结构:
- Team表:字段为id、name、manager_id
- Manager表:字段为id、company_id、user_id
- User表:存储最终需要的用户数据
已定义模型关联: - Team模型定义
manager()belongsTo关联到Manager模型 - Manager模型定义
user()belongsTo关联到User模型
原有查询从Team模型预加载manager.user关联,返回结果包含Team、Manager表冗余字段,目标是仅获取匹配条件的User集合。
可用实现方案
方案1:从User模型发起关联查询(推荐,性能最优)
原有写法从Team模型出发查询,天然会返回Team主模型数据,直接切换到User作为查询起点,通过whereHas做嵌套关联过滤,返回结果就是纯User模型集合,无任何冗余字段。
首先补全两个反向关联(如果没定义的话):
// 在Manager模型中添加和Team的反向关联 public function teams() { return $this->hasMany(Team::class); } // 在User模型中添加和Manager的反向关联 public function manager() { return $this->hasOne(Manager::class); }
控制器查询代码:
$managerUsers = User::whereHas('manager.teams', function ($query) use ($company) { // 过滤指定公司下的团队 $query->where('company_id', $company->id); })->get();
如果需要分页,直接在该查询后调用->paginate()即可,和普通查询用法完全一致
方案2:基于原有查询提取子级数据(无需修改模型关联)
如果不想新增模型关联,可以在原有查询结果上通过集合方法提取嵌套的User数据,同时做去重和空值过滤:
$managerUsers = Team::with('manager.user') ->where('company_id', $company->id) ->get() ->pluck('manager.user') // 提取嵌套层级中的user对象 ->filter() // 移除无关联user的空值条目 ->unique('id') // 按用户id去重,避免同一用户关联多个团队时重复返回 ->values(); // 重置集合连续键名
该方案缺点是会先查询Team、Manager表的全量字段,数据库IO开销更高,仅适合数据量较小的场景使用,且不支持直接调用框架分页方法。
方案3:Join联表直查(大数据量性能优先选择)
数据量达到十万级以上时,可以直接用联表查询,只查询User表字段,性能最高:
$managerUsers = User::query() ->join('managers', 'managers.user_id', '=', 'users.id') ->join('teams', 'teams.manager_id', '=', 'managers.id') ->where('teams.company_id', $company->id) ->select('users.*') // 显式指定只查询user表字段 ->distinct() // 联表可能产生重复数据,添加去重逻辑 ->get();
该方案不依赖模型关联定义,查询效率最高,但会丢失模型关联的动态加载能力,适合纯列表查询场景。
避坑说明
with()预加载方法的设计逻辑是返回主模型+所有指定关联的模型数据,只要从Team模型作为主模型发起查询,就必然会返回Team表字段,不存在只返回子关联的配置,需要返回纯子级数据时必须切换查询主模型或者做结果集提取。
内容的提问来源于stack exchange,提问作者Geeky Coder
相关产品推荐
相关产品推荐

