phplaraveleloquentormhas-many-through

Is Laravel hasManyThrough relationship correct for this purpose?


I've been reading through the documentation at Laravel's website but struggling to piece this all together.

We have users who can be assigned to specific roles and/or teams at different times. For example, User A could be assigned to Team 1 as Leader between two dates, then following that could be assigned to Team 1 as Operator. My thought was to use a hasManyThrough relationship to control this. Model example layout below.


-- user model

id

user_name

-- placements model (the through)

id

user_id

position_id

team_id

start (date)

end (date)

--positions

id

name


So the user and positions model will only have one row per record but placements model could have several rows per user but will never cross dates (can only be assigned to one placement at a time).

My goal is to be able to do things like, $users->position($date) or $users->currentPosition, as well as $team->members($date) or $team->currentMembers

I have tried a variety of relationships and hasManyThrough seems to make the most sense but i'm running into errors like this where the SQL makes me think the relationship is defined differently to how I was expecting.

class Team extends Model
{
    use HasFactory;
    public function members()
    {
        
        return $this->hasManyThrough(User::class, Placement::class, 'team_id');
    }
}

is equating to this SQL which makes me think i'm doing it wrong

select `users`.*, `placements`.`team_id` as `laravel_through_key` from `users` inner join `placements` on `placements`.`id` = `users`.`placement_id` where `placements`.`team_id` = 1

Any assistance would be greatly appreciated!

Thanks so much


Solution

  • You need to use belongsToMany relation where "placements" is your pivot table.

    User can have many positions and one position can belong to many users.

    hasOneThrough/HasManyThrough is not used with pivot tables and it is used in cases like this:

    User -> City -> Country
    

    If you wanted to access user's country you would have to write $user->city->country. When you define hasOneThrough you will have option to access country directly with $user->country