Laravel关联查询问题:通过whereHas筛选指定院校的Enrolment
首先,咱们来拆解你遇到的问题:你的whereHas使用问题主要出在两个地方——关联查询的字段名错误,以及可能的hasOneThrough关联定义不准确。另外,你当前的代码只做了查询,没有处理“未找到则创建Enrolment”的逻辑,我会帮你把完整流程补全。
1. 修正whereHas的查询逻辑
你的Enrolment(StudentBursaryEnrolment)模型通过hasOneThrough关联到AcademicInstitution,但你在whereHas里用了where('academic_institution','=','Pearson Institute'),这明显不对——AcademicInstitution表的名称字段应该是类似name的字段(而不是academic_institution),而且因为关联是通过Campus(AcademicInstitutionCampus)间接关联的,我们可以有两种写法:
写法一:直接通过academic_institution关联查询
如果你的hasOneThrough关联定义正确,直接用whereHas关联academic_institution即可,但要修正字段名:
$student = Student::findOrFail(1); $bursary = $student->bursaries()->first(); // 查询关联到Pearson Institute的Enrolment $enrolment = $bursary->enrolments() ->whereHas('academic_institution', function ($query) { // 这里替换成你AcademicInstitution表中存储名称的字段,比如name $query->where('name', 'Pearson Institute'); }) ->first(); // 用first()而不是get(),因为我们只需要单个匹配的记录
写法二:通过中间表Campus间接查询(更直观)
如果你对hasOneThrough的关联没把握,可以直接通过academic_institution_campus关联嵌套查询,这样逻辑更清晰:
$enrolment = $bursary->enrolments() ->whereHas('academic_institution_campus', function ($query) { $query->whereHas('academic_institution', function ($subQuery) { $subQuery->where('name', 'Pearson Institute'); }); }) ->first();
2. 检查并修正hasOneThrough关联定义
你的StudentBursaryEnrolment模型中的academic_institution关联可能参数顺序有误,hasOneThrough的参数需要严格对应表之间的外键关系,正确的定义应该是:
public function academic_institution() { return $this->hasOneThrough( \App\AcademicInstitution::class, \App\AcademicInstitutionCampus::class, 'id', // 中间表(AcademicInstitutionCampus)的主键,对应Enrolment的academic_institution_campus_id 'id', // 目标表(AcademicInstitution)的主键 'academic_institution_campus_id', // Enrolment表中关联到Campus的外键 'academic_institution_id' // Campus表中关联到AcademicInstitution的外键 ); }
如果这个关联定义错误,whereHas会无法正确关联到AcademicInstitution表。
3. 完整实现:查询或创建Enrolment并添加课程
接下来补全“未找到则创建Enrolment”的逻辑,同时创建课程:
$student = Student::findOrFail(1); $bursary = $student->bursaries()->first(); // 1. 查询目标Enrolment $enrolment = $bursary->enrolments() ->whereHas('academic_institution_campus', function ($query) { $query->whereHas('academic_institution', function ($subQuery) { $subQuery->where('name', 'Pearson Institute'); }); }) ->first(); // 2. 如果未找到,创建对应的Enrolment if (!$enrolment) { // 先找到Pearson Institute对应的AcademicInstitution和它的一个Campus $academicInstitution = \App\AcademicInstitution::where('name', 'Pearson Institute')->firstOrFail(); $campus = $academicInstitution->campuses()->firstOrFail(); // 假设AcademicInstitution有hasMany campuses的关联 // 创建Enrolment $enrolment = $bursary->enrolments()->create([ 'academic_institution_campus_id' => $campus->id, 'student_number' => $student->student_number, // 替换成你的Student表中对应的学号字段 // 其他必填字段请根据你的模型fillable补充 ]); } // 3. 在找到/创建的Enrolment下创建课程 $enrolment->courses()->create([ // 替换成你的Course模型需要的字段,比如: // 'course_name' => 'Math 101', // 'course_code' => 'MATH101' ]);
关键注意点
- 确保
AcademicInstitution模型和AcademicInstitutionCampus模型之间有正确的关联(比如AcademicInstitution有hasMany campuses的关联)。 - 替换代码中所有占位的字段名(比如
name、student_number)为你数据库中实际的字段名。 - 使用
first()而不是get(),因为我们只需要单个匹配的Enrolment记录。
内容的提问来源于stack exchange,提问作者SupaMonkey

