Laravel hasOneThrough relationship

Laravel hasOneThrough relationship

In this article let’s understand Laravel hasOneThrough relationship.

Laravel provides many relationship to work with multiple tables and one of them is Has One Through relationship.

Check below graphics to understand it in simple scenario.

hasOneThrough Laravel

What you can see from graphics is one Address will have only Building and one Building will have ony one Street. When we wants to access street from address we will have hasOneThrough relationship.

Let’s understand it through example where we have:

address table:
id
place
----------------------
building table:
id
name
address_id
----------------------
street table:
id
name
building_id
php artisan make:model Address -m
php artisan make:model Building -m
php artisan make:model Street -m

This will create three models and three migrations as below:

app/Models/
    Address.php
    Building.php
    Street.php

database/migrations/
    ***_create_addresses_table.php
    ***_create_buildings_table.php
    ***_create_streets_table.php

Open ***_create_addresses_table.php and modify as below:

public function up(): void
    {
       Schema::create('addresses', function (Blueprint $table) {
          $table->id();
          $table->string("place");
         $table->timestamps();
       });
    }

Check ***_create_buildings_table.php and modify as below:

public function up(): void
    {
       Schema::create('buildings', function (Blueprint $table) {
           $table->id();
           $table->string("name");
           $table->bigInteger('address_id')->unsigned()->index();
           $table->timestamps();
       });
    }

Check ***_create_streets_table.php and modify as below:

public function up(): void
    {
       Schema::create('streets', function (Blueprint $table) {
           $table->id();
           $table->string("name");
           $table->bigInteger('bulding_id')->unsigned()->index();
           $table->timestamps();
       });
}

Open app/Models/Address.php and modify as below:

<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Address extends Model
{
    use HasFactory;
    public function building()
    {
        return $this->hasOne('App\Building');
    }
    public function street()
    {
        return $this->hasOneThrough('App\Street', 'App\Building');
    }
}

You can see we have used hasOneThrough to connect street directly to Address through building.

If you will have different ids set than specific format you may need to define them as below:

// define if different naming used
return $this->hasOneThrough(
            Street::class,
            Building::class,
            'address_id', // Foreign key on building table...
            'building_id', // Foreign key on street table...
            'id', // Local key on addresses table...
            'id' // Local key on buildings table...
        );

Check app/Models/Building.php and modify as blow:

<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Building extends Model
{
    use HasFactory;
    public function street()
    {
        return $this->hasOne('App\Street');
    }
    public function address()
    {
        return $this->belongsTo('App\Address');
    }    
}

Check app/Models/Street.php and modify as blow:


namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Street extends Model
{
    use HasFactory;
    public function building()
    {
        return $this->belongsTo('App\Building');
    }
}

Run Migrations:

php artisan migrate

Use below methods in your controller to check how hasOneThrough relationship works:

//Fetch street of address

$address = Address::find(1);
echo $address->street->name;

//Fetch building of address

$address = Address::find(1);
echo $address->building->name;

//Fetch street of building

$building = Building::find(1);
echo $building->street->name;

So here we can directly retrieve cities of specific country of all the states.

That’s it on Laravel hasOneThrough relationship. Hope this finds you helpful.

See other articles on Laravel here.