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;