Has One Through

What is Has One Through?

  • A Has One Through relationship is used when a model is related to another model through an intermediate model.
  • Example:
  • A Manager has one Profile.
  • But the Profile is not directly linked to the Manager.
  • Instead, the link is through the Employee table.
  • So the connection looks like:
Manager → Worker → Salary

Flow diagram


Migration:

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

php artisan make:migration create_has_one_through_tables


Migration File

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

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

    // Salaries table
    Schema::create('salaries', function (Blueprint $table) {
        $table->id();
        $table->foreignId('worker_id')->constrained()->onDelete('cascade');
        $table->decimal('amount', 10, 2);
        $table->timestamps();
    });
}

public function down(): void
{
    Schema::dropIfExists('salaries');
    Schema::dropIfExists('workers');
    Schema::dropIfExists('managers');
}


Run Migration

php artisan migrate

Tables

managers table
id name created_at updated_at
workers table
id name manager_id created_at updated_at
salaries table
id amount worker_id created_at updated_at
  • workers.manager_id is a foreign key referencing managers.id.
  • salaries.worker_id is a foreign key referencing workers.id.

Model:

Generate a model with Artisan:

php artisan make:model Manager
php artisan make:model Employee
php artisan make:model Profile

This generates 3 files Manager.php , Employee.php and Profile.php


Manager.php

namespace App\Models;

use App\Models\Salary;
use App\Models\Worker;
use Illuminate\Database\Eloquent\Model;

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

    // HasOneThrough: Manager → Worker → Salary
    public function salary()
    {
        return $this->hasOneThrough(
            Salary::class,   // Final model
            Worker::class,   // Intermediate model
            'manager_id',    // Foreign key on workers table
            'worker_id',     // Foreign key on salaries table
            'id',            // Local key on managers
            'id'             // Local key on workers
        );
    }
}


Worker.php

namespace App\Models;

use App\Models\Salary;
use App\Models\Manager;
use Illuminate\Database\Eloquent\Model;

class Worker extends Model
{
    protected $fillable = ['name', 'manager_id'];

    public function manager()
    {
        return $this->belongsTo(Manager::class);
    }

    public function salary()
    {
        return $this->hasOne(Salary::class);
    }
}


Salary.php

namespace App\Models;

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

class Salary extends Model
{
    protected $fillable = ['amount', 'worker_id'];

    public function worker()
    {
        return $this->belongsTo(Worker::class);
    }
}

CRUD Examples

Create

use App\Models\Worker;
use App\Models\Salary;
use App\Models\Manager;

// Create a Manager
$manager = Manager::create(['name' => 'John']);

// Create Worker under Manager
$worker = Worker::create([
    'manager_id' => $manager->id,
    'name' => 'Contract Worker'
]);

// Assign Salary to Employee
$salary = Salary::create([
    'worker_id' => $worker->id,
    'amount' => 50000
]);

Tables

managers table
id name created_at updated_at
1 John 2025-08-25 05:39:34 2025-08-25 05:39:34
workers table
id name manager_id created_at updated_at
1 Contract Worker 1 2025-08-25 05:39:34 2025-08-25 05:39:34
salaries table
id amount worker_id created_at updated_at
1 50000 1 2025-08-25 05:39:34 2025-08-25 05:39:34

Read (Get Related Data)

use App\Models\Worker;
use App\Models\Manager;

// Get Manager's Salary through Worker
$managerSalary = Manager::find(1)->salary;
echo $managerSalary->amount . "<br>"; // Output: 50000

// Get Worker with Salary
$worker = Worker::first();
echo $worker->salary->amount . "<br>"; // Output: 50000

Update (Modify Data)

use App\Models\Worker;
use App\Models\Salary;
use App\Models\Manager;

// Update salary
$salary = Salary::where('worker_id', 1)->first();
$salary->update(['amount' => 60000]);

// Or directly
$worker = Worker::find(1);
$worker->salary->amount = 65000;
$worker->salary->save();

// Or via Manager using HasOneThrough
$manager = Manager::find(1);
$manager->salary->amount = 70000;
$manager->salary->save();

4. Delete (Remove Data)

use App\Models\Worker;
use App\Models\Salary;
use App\Models\Manager;

// Delete Salary
$salary = Salary::first();
$salary->delete();

// Delete Salary via Worker using hasOne
$worker = Worker::first();
$worker->salary->delete();

// Delete Salary via Manager using hasOne
$manager = Manager::first();
$manager->salary->delete();

// Delete Employee (will cascade Salary also if set cascade)
$worker = Worker::first();
$worker->delete();

// Delete Manager (cascade removes workers & their salaries)
$manager = Manager::first();
$manager->delete();

Eager loading (optimizes queries)

use App\Models\Worker;
use App\Models\Manager;

// Get Manager's Salary through Worker
$managerSalary = Manager::with('salary')->find(1);
echo $managerSalary->amount . "<br>"; // Output: 50000

// Get Worker with Salary
$worker = Worker::with('salary')->first();
echo $worker->salary->amount . "<br>"; // Output: 50000

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


Whereisstuff is simple learing platform for beginer to advance level to improve there skills in technologies.we will provide all material free of cost.you can write a code in runkit workspace and we provide some extrac features also, you agree to have read and accepted our terms of use, cookie and privacy policy.
© Copyright 2024 www.whereisstuff.com. All rights reserved. Developed by whereisstuff Tech.