Search code examples
laraveleloquent-relationship

Laravel relationships 3-way, ternary relationship


Given the following Laravel 10 models. Users(id,...), Vehicles (id, primary_user), UserVehicles (user_id,vehicle_id,vehicle_role_id), VehicleRoles (id,name)

I am trying to find the correct laravel relationships to associate the following.

  • A user can have many vehicles.
  • A vehicle can have many users
  • A vehicle has only one primary_user
  • A user has one role with a vehicle (fleet manager, driver, owner))
  • A userVehicle has one role, one vehicle and one user.

User model

public function vehicles()
{
    return $this->belongsToMany(
        Vehicle::class, 
        'user_vehicle', 
        'user_id', 
        'vehicle_id'
    )->withPivot('vehicle_role_id')->as('role');
}

vehicles Model

public function users()
{
    return $this->belongsToMany(User::class)->withPivot('vehicle_role_id');
}

User vehicle model

public function user(): BelongsTo
{
    return $this->belongsTo(User::class);
}

public function vehicle(): HasOne
{
    return $this->hasOne(Vehicle::class,'id','vehicle_id')
        ->whereNull('deleted_at');
}

SQL

select u.name, uv.vehicle_id, vr.name 
from users u 
join user_vehicle uv on uv.user_id = u.id 
join vehicle_roles vr on vr.id = uv.vehicle_role_id
where u.id =1 and vehicle_id = 10

ER Diagram

I just cannot seem to get my head around how to associate the laravel models I am having issue trying to get the role from the uservehicle relation Which Model do i define the relationship on and which relationship type

I have tried hasOneThrough, And tried using belongsToMany with pivots and without.

$user = User::find(1); $vehicles = $user->vehicles;

$vehicles[1]->role->name; //I want to return the role name

UPDATED my User model above : I have been able to get the roleID using the pivot but NOT the name

$u = User::find(1); $vs = $u->vehicles; $v = $vs[1]->role;

{
  user_id: 1,
  vehicle_id: 10,
  vehicle_role_id: 3,
},
  

Solution

  • User Model

    public function vehicles(): HasMany
    {
        return $this->hasMany(UserVehicle::class, 'user_id');
    }
    

    Vehicle Model:

    public function users(): BelongsToMany
    {
        return $this->belongsToMany(User::class, 'user_vehicles')->withPivot('vehicle_role_id')->withTimestamps();
    }
    
    public function primaryUser(): HasOne
    {
        return $this->hasOne(UserVehicle::class, 'vehicle_id')->where('is_primary', true);
    }
    

    VehicleRoles Model:

    public function userVehicles(): HasMany
    {
        return $this->hasMany(UserVehicle::class, 'vehicle_role_id');
    }
    

    UserVehicle Model:

    public function user(): BelongsTo
    {
        return $this->belongsTo(User::class, 'user_id');
    }
    
    public function vehicle(): BelongsTo
    {
        return $this->belongsTo(Vehicle::class, 'vehicle_id');
    }
    
    public function role(): BelongsTo
    {
        return $this->belongsTo(VehicleRoles::class, 'vehicle_role_id');
    }
    

    you can get the role name for a user's vehicle using

    $user = User::find(1);
    $vehicles = $user->vehicles;
    
    foreach ($vehicles as $vehicle) {
        $roleName = $vehicle->pivot->role->name;
        echo "Role name for vehicle " . $vehicle->id . ": " . $roleName . "\n";
    }