You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 05:37:46