A many-to-many relationship means multiple records in one table can be associated with multiple records in another table.

Real-World Examples

  • A Student can enroll in many Courses
  • A Course can have many Students
  • A User can have many Roles
  • A Product can have many Tags

1.Database Design (VERY IMPORTANT)

Many-to-Many relationships require 3 tables:

  1. First main table
  2. Second main table
  3. Pivot table (middle table)

Example: Students & Courses

students table

id
name

courses table

id
title

course_student (pivot table)

student_id
course_id

📌 Pivot table name rule:

  • Alphabetical order
  • Singular table names

course_student (NOT student_course)

2.Create Models & Migrations

php artisan make:model Student -m
php artisan make:model Course -m

Pivot migration

php artisan make:migration create_course_student_table

Pivot Table Migration Code

Schema::create('course_student', function (Blueprint $table) {
$table->id();
$table->foreignId('student_id')->constrained()->cascadeOnDelete();
$table->foreignId('course_id')->constrained()->cascadeOnDelete();
$table->timestamps();
});

Run migrations:

php artisan migrate

3.Define Relationship in Models

Student Model

class Student extends Model
{
public function courses()
{
return $this->belongsToMany(Course::class);
}
}

Course Model

class Course extends Model
{
public function students()
{
return $this->belongsToMany(Student::class);
}
}

4.How Laravel Understands the Relationship

Laravel automatically assumes:

Item Value
Pivot table course_student
Foreign key (student) student_id
Foreign key (course) course_id

If your names are custom, you must define them manually.

5.Fetch Related Data

Get all courses of a student

$student = Student::find(1);
$courses = $student->courses;

Loop through data

foreach ($student->courses as $course) {
echo $course->title;
}

Get all students of a course

$course = Course::find(1);
$course->students;

6.Eager Loading (Performance Optimization)

❌ Wrong way (N+1 problem)

$students = Student::all();

foreach ($students as $student) {
$student->courses;
}

✅ Correct way

$students = Student::with('courses')->get();

7.Attach Data to Pivot Table

Enroll a student in a course

$student->courses()->attach(1);

Attach multiple courses

$student->courses()->attach([1, 2, 3]);

8.Detach Data

Remove one course

$student->courses()->detach(2);

Remove all courses

$student->courses()->detach();

9. Sync Data (Most Used)

Sync replaces existing records

$student->courses()->sync([1, 2, 3]);

✔ Removes old
✔ Inserts new
✔ Keeps matched

10. Pivot Table With Extra Columns

Example: course_student

student_id
course_id
enrolled_at
marks

Migration

$table->integer('marks')->nullable();
$table->timestamp('enrolled_at')->nullable();

Attach With Extra Data

$student->courses()->attach(1, [
'marks' => 85,
'enrolled_at' => now()
]);

Access Pivot Data

foreach ($student->courses as $course) {
echo $course->pivot->marks;
}

Tell Laravel to Load Pivot Fields

return $this->belongsToMany(Course::class)
->withPivot('marks', 'enrolled_at')
->withTimestamps();

11.Update Pivot Data

$student->courses()->updateExistingPivot(1, [
'marks' => 90
]);

12.Custom Pivot Table Name

return $this->belongsToMany(
Course::class,
'enrollments',
'student_id',
'course_id'
);

13. Conditional Queries on Relationship

$student->courses()->where('status', 'active')->get();

14.Count Related Records

$student->courses()->count();

Optimized:

Student::withCount('courses')->get();

15.Delete Pivot Records Automatically

$student->courses()->detach();

16.Many-to-Many in Controller Example

class EnrollmentController extends Controller
{
public function enroll(Request $request)
{
$student = Student::findOrFail($request->student_id);
$student->courses()->syncWithoutDetaching($request->course_ids);return response()->json(['message' => 'Enrolled successfully']);
}
}

Categorized in:

Laravel,