Home > front end >  hasManyThrough with morph
hasManyThrough with morph

Time:02-14

I have a problem that I have been stuck with for days, I'd appreciate any help from you guys.

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.

Thanks in advance...

CodePudding user response:

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'));
    });
}

CodePudding user response:

Use whereHasMorph:

$user = ...

$projects = Project::whereHasMorph(
    'assignable',
    [Team::class, User::class],
    function ($query, $type) use($user) {
        if($type === Team::class){
            $query->whereRelation('users', 'id', $user->id);
        }else{
            $query->where('id', $user->id);
        } 
    }
)->get();

Or split into 2 queries and merge together:

$projects = $user->projects;
$teams = $user->teams()->with('projects')->get();
$teams_projects = $teams->pluck('projects')->collapse();
$projects = $projects->merge($teams_projects);
  • Related