Advertisement

Google Ad Slot: content-top

Has Many Through


What is Has Many Through?

A Has Many Through relationship is used when one model is related to many records of another model through an intermediate model.

👉 Example:

  • A Hospital has many Patients.
  • But Patients are not directly linked to Hospital.
  • Instead, they are linked through Doctors (because each Doctor belongs to a Hospital and has Patients).

So the connection looks like:

 Hospital → Doctors → Patients

Flow diagram


Migration:

Use this command to generate a migration for creating a tables:

php artisan make:migration create_has_many_through_tables


Migration File

public function up(): void
{
    // Hospitals table
    Schema::create('hospitals', function (Blueprint $table) {
        $table->id();
        $table->string('name');
        $table->timestamps();
    });

    // Doctors table
    Schema::create('doctors', function (Blueprint $table) {
        $table->id();
        $table->foreignId('hospital_id')->constrained()->onDelete('cascade');
        $table->string('name');
        $table->timestamps();
    });

    // Patients table
    Schema::create('patients', function (Blueprint $table) {
        $table->id();
        $table->foreignId('doctor_id')->constrained()->onDelete('cascade');
        $table->string('name');
        $table->timestamps();
    });
}

public function down(): void
{
    Schema::dropIfExists('patients');
    Schema::dropIfExists('doctors');
    Schema::dropIfExists('hospitals');
}


Run Migration

php artisan migrate

Tables

hospitals table
id name created_at updated_at
doctors table
id name hospital_id created_at updated_at
patients table
id name doctor_id created_at updated_at
  • doctors.hospital_id is a foreign key referencing hospitals.id.
  • patients.doctor_id is a foreign key referencing doctors.id.

Model:

Generate a model with Artisan:

php artisan make:model Hospital
php artisan make:model Doctor
php artisan make:model Patient

This generates 3 files Hospital.php , Doctor.php and Patient.php


 Hospital.php 

namespace App\Models;

use App\Models\Doctor;
use App\Models\Patient;
use Illuminate\Database\Eloquent\Model;

class Hospital extends Model
{
    protected $fillable = ['name'];

    // HasManyThrough: Hospital → Doctors → Patients
    public function patients()
    {
        return $this->hasManyThrough(
            Patient::class,   // Final model
            Doctor::class,    // Intermediate model
            'hospital_id',    // Foreign key on doctors
            'doctor_id',      // Foreign key on patients
            'id',             // Local key on hospitals
            'id'              // Local key on doctors
        );
    }

    public function doctors()
    {
        return $this->hasMany(Doctor::class);
    }
}


Doctor.php

namespace App\Models;

use App\Models\Patient;
use App\Models\Hospital;
use Illuminate\Database\Eloquent\Model;

class Doctor extends Model
{
    protected $fillable = ['name', 'hospital_id'];

    public function hospital()
    {
        return $this->belongsTo(Hospital::class);
    }

    public function patients()
    {
        return $this->hasMany(Patient::class);
    }
}


Patient.php

namespace App\Models;

use App\Models\Doctor;
use Illuminate\Database\Eloquent\Model;

class Patient extends Model
{
    protected $fillable = ['name', 'doctor_id'];

    public function doctor()
    {
        return $this->belongsTo(Doctor::class);
    }
}

CRUD Examples

Create

use App\Models\Patient;
use App\Models\Hospital;
use App\Models\Doctor;

// Create a Hospital
$hospital = Hospital::create(['name' => 'City Care']);

// Create Doctor under Hospital
$doctor = Doctor::create([
    'hospital_id' => $hospital->id,
    'name' => 'Dr. John'
]);

// Assign Patient to Doctor
$patient = Patient::create([
    'doctor_id' => $doctor->id,
    'name' => 'Alice'
]);
Output
hospitals table
id name created_at updated_at
1 City Care 2025-08-25 14:55:55 2025-08-25 14:55:55
doctors table
id name hospital_id created_at updated_at
1 Dr. John 1 2025-08-25 14:55:55 2025-08-25 14:55:55
patients table
id name doctor_id created_at updated_at
1 Alice 1 2025-08-25 14:55:55 2025-08-25 14:55:55

Read (Get Related Data)

use App\Models\Patient;
use App\Models\Hospital;
use App\Models\Doctor;

// Get Hospital's Doctors 
$hospital = Hospital::find(1);
foreach ($hospital->doctors as $doctor) {
  echo $doctor->name;
}

// Get Hospital's Patients through Doctors
$hospital = Hospital::find(1);
foreach ($hospital->patients as $patient) {
  echo $patient->name;
}

// Get Doctor with Patients
$doctor = Doctor::find(1);
foreach ($doctor->patients as $patient) {
  echo $patient->name;
}

Update (Modify Data)

use App\Models\Patient;
use App\Models\Hospital;
use App\Models\Doctor;

// Update Patient 
$salary = Patient::where('doctor_id', 1)->first();
$salary->update(['name' => 'kenny']);

// Or directly
$hospital = Hospital::find(1);
$hospital->patients()->where('id', 1)->update(['name' => 'Alice Updated']);



Delete (Remove Data)

use App\Models\Patient;
use App\Models\Hospital;
use App\Models\Doctor;

// Delete Patient
$patient = Patient::first();
$patient->delete();

// Delete Patient via Doctor using hasMany
$doctor = Doctor::first();
$doctor->patients()->where('id', 1)->delete();

// Delete Doctor (will cascade Patients also if set cascade)
$doctor = Doctor::first();
$doctor->delete();

// Delete Hospital (cascade removes Doctors & their Patients)
$hospital = Hospital::first();
$hospital->delete();

Eager loading (optimizes queries)

use App\Models\Patient;
use App\Models\Hospital;
use App\Models\Doctor;

// Get Hospital's Doctors 
$hospital = Hospital::with('doctors')->find(1);
foreach ($hospital->doctors as $doctor) {
  echo $doctor->name;
}

// Get Hospital's Patients through Doctors
$hospital = Hospital::with('patients')->find(1);
foreach ($hospital->patients as $patient) {
  echo $patient->name;
}

// Get Doctor with Patients
$doctor = Doctor::with('patients')->find(1);
foreach ($doctor->patients as $patient) {
  echo $patient->name;
}

Always use with() when fetching related data in bulk.