没有特定角色的用户选择优美的多对多

5
所以我有一个具有多对多关系的 UserRole 模型,我有3个角色:superadminmoderator,还有4个用户: JohnMikeJamesLarryJohnsuperMikeadminmoderator 角色,James 是一个 adminLarry 是一个 moderator。为了显示没有某些角色的用户,我创建了这个范围:
public function scopeDoesntHaveRoles($query, $roles = [], $column = 'id') {
    return $query->whereDoesntHave('roles')->orWhereHas('roles', function ($q) use ($roles, $column) {
        $q->whereNotIn($column, $roles);
    });
}

当我调用 User::doesntHaveRoles([1])->lists('name', 'id') 来获取没有超级角色的用户时,它可以正常运行并返回:

{"2":"Mike","3":"James","4":"Larry"}

但是,当我尝试列出没有admin角色的用户时,User::doesntHaveRoles([2])->lists('name', 'id'),是的,James不在其中,但是实际上拥有admin角色的Mike却出现了:

{"1":"John","2":"Mike","4":"Larry"}

我认为这是因为Mike也拥有管理员角色,您是否在我的范围中看到了什么问题?或者您有其他解决方案吗?
谢谢
编辑:以下是我的枢轴模式。
Schema::create('user_roles', function (Blueprint $table) {
    $table->integer('user_id')->unsigned();
    $table->integer('role_id')->unsigned();

    $table->primary([
        'user_id', 'role_id'
    ]);
});

User 模型

public function roles()
{
    return $this->belongsToMany(Role::class, 'user_roles');
}

角色模型
public function users()
{
    return $this->belongsToMany(User::class, 'user_roles');
}

你能描述一下rolesusers表之间的数据透视表模式吗?如果你遵循惯例,应该在其中有一个role_id列,你可以在这个列上调用whereNotIn方法。 - shempignon
@shempignon 这只是一个基本的数据透视表,请参见已编辑的帖子。 - Rifki
1个回答

2

我建议使用whereNotIn而不是whereDoesntHave

假设有一个变量$role存储了一个Role,你可以通过以下代码获得没有这个角色的所有用户:

/* @var Role $role */
User::whereNotIn(function('id', $query) use ($role) {
    $query->select('user_id')
        ->from('user_roles')
        ->where('role_id', $role->id);
});

内部查询将返回具有给定角色的所有用户的ID。使用whereNotIn将返回相反的用户集合。将创建以下查询:

select *
from users
where user_id not in (
    select user_id
    from user_roles
    where role_id = ?
);

现在有一个存储在$roles中的角色集合,您可以使用以下代码获取没有任何这些角色的所有用户:

/* @var Collection|Role[] $roles */
User::whereNotIn(function('id', $query) use ($roles) {
    $query->select('user_id')
        ->from('user_roles')
        ->whereIn('role_id', $roles->pluck('id');
});

内部选择将返回具有集合中任一角色的所有用户的ID。使用whereNotIn,您将再次获得相反的结果。您还可以使用角色ID数组而不是$roles->pluck('id')
构建器将创建一个查询,如下所示:
select *
from users
where user_id not in (
    select user_id
    from user_roles
    where role_id in (?, ?, ..)
);

最终我得到了一个绝妙的解决方案,感谢@PaulSpiegel,这正是我所寻找的。 - Rifki

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接