Laravel多关联关系自定义条件查询:筛选满足前置课程要求但未修目标课程的学生
问题描述
我现在有三个模型:Person、Course 和 CourseType,它们的关联关系是:
Person和Course是belongsToMany关联(对应关联方法是studentCourses)Course和CourseType是belongsTo关联
现在需要实现一个查询:筛选出满足目标课程前置要求并且未修读该目标课程的学生。目标课程和前置课程都通过 course_slug 字段标识。
我已经能写出查询已修读指定课程学生的代码,但卡在前置要求+未修读的逻辑上,现有代码如下:
return Person::whereHas('studentCourses', function($query){ $query->whereHas('courseType', function ($sub_query) { $sub_query->where('id_course', $this->course_slug); }); })->get();
解决方案
别担心,我来帮你拆解这个需求并给出可行的实现方案。我们需要同时满足两个核心条件:学生已完成所有前置课程,以及学生还没修读目标课程。下面分两种常见场景给出代码:
场景1:明确知道前置课程的slug列表
如果已经确定了目标课程的前置课程(比如必须先修course-a和course-b),可以直接用以下代码:
// 先定义目标课程和前置课程的slug $targetCourseSlug = $this->course_slug; $prerequisiteSlugs = ['course-a', 'course-b']; return Person::query() // 确保学生修读了所有前置课程 ->where(function($query) use ($prerequisiteSlugs) { foreach ($prerequisiteSlugs as $slug) { $query->whereHas('studentCourses', function($q) use ($slug) { $q->whereHas('courseType', function($subQ) use ($slug) { $subQ->where('id_course', $slug); }); }); } }) // 排除已经修读了目标课程的学生 ->whereDoesntHave('studentCourses', function($query) use ($targetCourseSlug) { $query->whereHas('courseType', function($sub_query) use ($targetCourseSlug) { $sub_query->where('id_course', $targetCourseSlug); }); }) ->get();
场景2:通过模型关联动态获取前置课程
如果你的CourseType模型已经定义了和自身的关联(比如prerequisites,用来存储课程的前置要求),可以先动态获取目标课程的前置列表,再执行查询:
// 先拿到目标课程的模型实例 $targetCourse = CourseType::where('id_course', $this->course_slug)->firstOrFail(); // 假设CourseType有belongsToMany关联prerequisites,取出所有前置课程的slug $prerequisiteSlugs = $targetCourse->prerequisites->pluck('id_course')->toArray(); return Person::query() ->where(function($query) use ($prerequisiteSlugs) { foreach ($prerequisiteSlugs as $slug) { $query->whereHas('studentCourses', function($q) use ($slug) { $q->whereHas('courseType', function($subQ) use ($slug) { $subQ->where('id_course', $slug); }); }); } }) ->whereDoesntHave('studentCourses', function($query) use ($targetCourse) { $query->whereHas('courseType', function($sub_query) use ($targetCourse) { $sub_query->where('id_course', $targetCourse->id_course); }); }) ->get();
关键逻辑说明
whereHas用来检查学生是否修读了某门课程,循环前置课程列表就能保证所有前置要求都被满足(相当于AND逻辑)whereDoesntHave是whereHas的反向用法,专门用来排除已经修读了目标课程的学生- 外层包裹的
where(function($query) { ... })确保所有前置课程的条件同时成立,不会出现只满足部分前置的情况
内容的提问来源于stack exchange,提问作者Atomos
相关产品推荐
相关产品推荐

