Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

andyjameswhite's avatar

hasOneThrough and hasManyThrough

I've got a bit confused on hasOneThrough and hasManyThrough

I've setup a very simple data structure

  1. users - with a primary key of id
  2. projects - with a primary key of id and foreign key of user_id (effectively the "owner" of the project)
  3. sites - with a primary key of id and foreign key of project_id
  4. project_user - a pivot table with any other users in it related to the project

all pretty standard and the regular relationships, belongs to, has many etc. work fine

So I thought I'd define the HasThrough relationships to keep things easier when building things out.

So on my User Model this works fine

    public function sites(): HasManyThrough
    {
        return $this->hasManyThrough(Site::class, Project::class);
    }

I was surprised find on the Site Model to get the "owner" that I needed to adjust the keys but did so and it seems work ok i.e. it adds the owner as an object under the main Site object

    public function owner(): HasOneThrough
    {
        return $this->hasOneThrough(
            User::class,
            Project::class, 
            'id', 
            'id', 
            'project_id',
            'user_id'
        );
    }

Then finally to get all other users in the pivot table project_user I added the following to the Site Model


    public function users(): HasManyThrough
    {
        return $this->hasManyThrough(
            User::class, 
            Project::class,
            'id',
            'id',
            'project_id', 
            'user_id'
        );
    }

This adds a collection under the Site object, but just with the owner.

Having had a good look at the relevant section in the docs I can see I'm doing something wrong, it doesn't mention a pivot table there.

https://laravel.com/docs/11.x/eloquent-relationships#has-many-through

I my question really is; Is there a defined way in Eloquent Relationships to do what I'm trying to do, or do I just need to create my own method instead

0 likes
1 reply
LaryAI's avatar
Level 58

In your scenario, you're trying to use hasManyThrough to access users through a pivot table, which isn't directly supported by hasManyThrough. The hasManyThrough and hasOneThrough relationships are designed for traversing through a single intermediary model, not through a pivot table. For pivot tables, you should use belongsToMany.

Here's how you can set up the relationships correctly:

  1. User Model: To get all sites through projects owned by the user, your existing hasManyThrough relationship is correct.

    public function sites(): HasManyThrough
    {
        return $this->hasManyThrough(Site::class, Project::class);
    }
    
  2. Site Model: To get the owner of the site, your hasOneThrough relationship is also correct, assuming the keys are set up properly.

    public function owner(): HasOneThrough
    {
        return $this->hasOneThrough(
            User::class,
            Project::class, 
            'id', // Foreign key on the projects table...
            'id', // Foreign key on the users table...
            'project_id', // Local key on the sites table...
            'user_id' // Local key on the projects table...
        );
    }
    
  3. Site Model: To get all users associated with a project through the project_user pivot table, you should use belongsToMany.

    public function users(): BelongsToMany
    {
        return $this->belongsToMany(User::class, 'project_user', 'project_id', 'user_id');
    }
    

    However, since the project_user table is related to projects, you should define this relationship on the Project model instead:

    // In Project Model
    public function users(): BelongsToMany
    {
        return $this->belongsToMany(User::class, 'project_user', 'project_id', 'user_id');
    }
    

    Then, you can access the users from a site like this:

    // In Site Model
    public function projectUsers(): BelongsToMany
    {
        return $this->project->users();
    }
    

    Ensure that you have a project relationship defined in the Site model:

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

By using belongsToMany for pivot tables, you can correctly access all users associated with a project. If you need to access these users directly from the Site model, you can create a custom method or use the project relationship to access the users.

Please or to participate in this conversation.