Laravel关联查询问题:如何仅关联特定状态的Task并过滤结果
问题:Laravel中如何过滤预加载的关联Task模型
我有如下查询代码:
$projects = Project::with('tasks') ->select('projects.*') ->join('tasks', 'projects.id', '=', 'tasks.project_id')
我需要仅关联状态为特定值(例如done)的Task。目前尝试的写法如下:
$projects = Project::with('tasks', 'company') ->select('projects.*') ->join('tasks', 'projects.id', '=', 'tasks.project_id') ->where(function (Builder $query) use ($projectsListRequest){ if (empty($projectsListRequest->taskStatus)) return; $query->where('tasks.status', $projectsListRequest->taskStatus); })
但结果仍包含状态不符的Task。我猜测需要在join中添加额外条件,另外我觉得无论如何处理,tasks都会被预加载,因为模型中的tasks方法未添加过滤条件,但这只是我的猜测。
解决方案
你的猜测是对的:with('tasks')会直接加载模型关联方法定义的所有Task,主查询的where只会过滤出符合条件的Project,但不会影响预加载的关联数据。要解决这个问题,需要从过滤预加载关联和筛选目标Project两个层面处理:
1. 过滤预加载的Task关联
在with方法中传入闭包,专门对预加载的tasks进行过滤:
$projects = Project::with([ 'tasks' => function ($query) use ($projectsListRequest) { // 仅当有状态参数时,过滤task状态 if (!empty($projectsListRequest->taskStatus)) { $query->where('status', $projectsListRequest->taskStatus); } }, 'company' // 保留原有的company关联 ])
2. 筛选出包含符合条件Task的Project
如果你的需求是只获取至少有一个符合状态Task的Project,推荐用whereHas替代join,写法更简洁且避免重复数据:
->when(!empty($projectsListRequest->taskStatus), function ($query) use ($projectsListRequest) { $query->whereHas('tasks', function ($subQuery) use ($projectsListRequest) { $subQuery->where('status', $projectsListRequest->taskStatus); }); })
如果一定要用join,可以在join语句中添加条件,同时记得加distinct()避免Project重复:
->join('tasks', function ($join) use ($projectsListRequest) { $join->on('projects.id', '=', 'tasks.project_id'); if (!empty($projectsListRequest->taskStatus)) { $join->where('tasks.status', $projectsListRequest->taskStatus); } }) ->distinct()
完整代码示例
$projects = Project::with([ 'tasks' => function ($query) use ($projectsListRequest) { if (!empty($projectsListRequest->taskStatus)) { $query->where('status', $projectsListRequest->taskStatus); } }, 'company' ]) ->when(!empty($projectsListRequest->taskStatus), function ($query) use ($projectsListRequest) { $query->whereHas('tasks', function ($subQuery) use ($projectsListRequest) { $subQuery->where('status', $projectsListRequest->taskStatus); }); }) ->select('projects.*') ->get();
内容的提问来源于stack exchange,提问作者inf3rno
相关产品推荐
相关产品推荐

