Search code examples
laraveldatabaseeloquentpolymorphic-relationship

hasManyThrough with morph


I have a projects table that either can be assigned to a user or a team, this is the structure:

id
assignable_id
assignable_type

class Project extends Model {
    public function assignable(): MorphTo
    {
        return $this->morphTo();
    }
}

and my team table contains members with a pivot table:

id

class Team extends Model {

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

    public function projects(): MorphMany
    {
        return $this->morphMany(Project::class, 'assignable');
    }
}

// team_user table
team_id
user_id

and finally my user table:

id

class User extends Model {

    public function teams(): BelongsToMany
    {
        return $this->belongsToMany(Team::class);
    }

    public function projects(): MorphMany
    {
        return $this->morphMany(Project::class, 'assignable');
    }
}

Now the problem is I want to get all projects that have been assigned to a user either directly or through a team, as expected $user()->projects return the ones that directly have been assigned to the user.

I tried many solutions but none has worked for me yet.


Solution

  • So you actually need something like this:

    public function project()
    {
        return $this->hasManyThrough('App\Project', 'App\User', 'assignable_id')
            ->where(
                'assignable_type', 
                array_search(static::class, Relation::morphMap()) ?: static::class
            );
    }
    

    or

    public function project()
    {
        return $this->belongsToMany(Project::class, 'assignable', 'assignable_id', 'project_id')
            ->where('assignable_type', static::class);
    }
    

    Ref: Laravel Polymorphic Relations Has Many Through

    Your working solution:

    public function projects() {
        return $this->hasManyThrough(
            Project::class,
            User::class,
            'id',
            'assignable_id',
        )->where(
            'assignable_type', User::class
        )->orWhere(function ($q) {
            $q->where('assignable_type', Team::class)
            ->whereIn('assignable_id', $this->teams->pluck('id'));
        });
    }