The "has-one-through" relationship links models through a single intermediate relation.
For example, in a vehicle repair shop application, each Mechanic may have one Car, and each Car may have one Owner. While the Mechanic and the Owner have no direct connection, the Mechanic can access the Owner through the Car itself. Let's look at the tables necessary to define this relationship:
mechanics
id - integer
name - string
cars
id - integer
model - string
mechanic_id - integer
owners
id - integer
name - string
car_id - integer
Now that we have examined the table structure for the relationship, let's define the relationship on the Mechanic model:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Mechanic extends Model
{
/**
* Get the car's owner.
*/
public function carOwner()
{
return $this->hasOneThrough('App\Models\Owner', 'App\Models\Car');
}
}
The first argument passed to the hasOneThrough method is the name of the final model we wish to access, while the second argument is the name of the intermediate model.
Typical Eloquent foreign key conventions will be used when performing the relationship's queries. If you would like to customize the keys of the relationship, you may pass them as the third and fourth arguments to the hasOneThrough method. The third argument is the name of the foreign key on the intermediate model. The fourth argument is the name of the foreign key on the final model. The fifth argument is the local key, while the sixth argument is the local key of the intermediate model:
class Mechanic extends Model
{
/**
* Get the car's owner.
*/
public function carOwner()
{
return $this->hasOneThrough(
'App\Models\Owner',
'App\Models\Car',
'mechanic_id', // Foreign key on cars table...
'car_id', // Foreign key on owners table...
'id', // Local key on mechanics table...
'id' // Local key on cars table...
);
}
}
Example How to use:
$mechanic = Mechanic::find(1);
$carOwner = $mechanic ->carOwner;
Example 2:
Consider we have User Model, Reviewer Model and Activity Model with structure :
users
id - integer
reviewer_id - integer
reviewers
id - integer
activities
id - integer
user_id - integer
Each reviewer has one user, and each user is associated with one user activity record. If we want to get activity from reviewer, there is no reviewer_id column in activities table, in such case we can use hasOneThrough relation. Let's define the relationship now :
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Reviewer extends Model
{
/**
* Get the reviewer's activity.
*/
public function activity()
{
return $this->hasOneThrough('App\Activity', 'App\User');
}
}
To understand more, lets use Laravel conventional terms on the models. In this case the Activity is the final model we would need to relate, while User is the intermediate model.
As all other relationships in Laravel, we can also explicitely specify the foreign keys while definining hasOneThrough relation, Let's do that for this example :
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Reviewer extends Model
{
/**
* Get the reviewer's activity.
*/
public function activity()
{
return $this->hasOneThrough(
'App\Activity',
'App\User',
'reviewer_id', // Foreign key on users table...
'user_id', // Foreign key on activities table...
'id', // Local key on reviewers table...
'id' // Local key on users table...
);
}
}
Now you can easily do :
$reviewer = Reviewer::first();
$activity = $reviewer->activity;