昨天我們學了一對一(One-to-One)和一對多(One-to-Many)今天來了解稍微進階的兩種關係:多對多(Many-to-Many) 和 HasManyThrough
這種情況就需要第三張「中間表」(Pivot Table)
students
:學生courses
:課程course_student
:中間表,存放 student_id
和 course_id
Schema::create('course_student', function (Blueprint $table) {
$table->id();
$table->foreignId('student_id')->constrained()->onDelete('cascade');
$table->foreignId('course_id')->constrained()->onDelete('cascade');
$table->timestamps();
});
// Student.php
public function courses()
{
return $this->belongsToMany(Course::class);
}
// Course.php
public function students()
{
return $this->belongsToMany(Student::class);
}
// 學生選課
$student = Student::find(1);
$student->courses()->attach(2); // 選 ID=2 的課
$student->courses()->attach([3, 4]); // 選多門課
// 退選
$student->courses()->detach(3);
// 查詢學生的課程
$courses = $student->courses;
// 查詢課程的學生
$course = Course::find(2);
$students = $course->students;
💡 Pivot 欄位
如果中間表還有額外欄位(例如成績 grade
),可以用:
$student->courses()->attach(2, ['grade' => 90]);
取出:
foreach ($student->courses as $course) {
echo $course->pivot->grade;
}
countries
:國家users
:有 country_id
posts
:有 user_id
// Country.php
public function posts()
{
return $this->hasManyThrough(Post::class, User::class);
}
$country = Country::find(1);
$posts = $country->posts; // 直接拿到這個國家的所有文章
多對多的中間表命名
預設是兩個單數表名按字母排序,例如 course_student
,不要自己亂改。
HasManyThrough 的中間 Model
要正確設定「目標 Model」和「中間 Model」,順序不能反。
attach
/ detach
/ sync
attach()
→ 新增關聯detach()
→ 移除關聯sync()
→ 同步關聯(會刪掉不在陣列中的項目)