Laravel中获取认证用户所有权限的最优实现方案咨询(含belongsToManyThrough替代方案探讨)
Laravel中获取认证用户所有权限的最优实现方案咨询(含belongsToManyThrough替代方案探讨)
哥们,你这个场景我太熟了!既然已经搭好了角色-权限的多对多关联结构,完全没必要用嵌套foreach那种N+1查询的低效方式,咱们直接用Eloquent关联或者查询构建器一次搞定,性能拉满!
首先先确认下你的模型关联(你说已经定义了,但我再明确下,方便后续理解)
User模型和Role模型是多对多关联:public function roles() { return $this->belongsToMany(Role::class); }Role模型和Ability模型是多对多关联:public function abilities() { return $this->belongsToMany(Ability::class); }
方案一:自定义多对多跨表关联(推荐)
Laravel原生没有belongsToManyThrough,但咱们可以手动拼接关联关系,直接在User模型里定义一个abilities方法,让它直接关联到Ability表,通过中间的role_user和ability_role表查询:
// User模型中新增 public function abilities() { return $this->belongsToMany(Ability::class) ->join('ability_role', 'roles.id', '=', 'ability_role.role_id') ->join('abilities', 'ability_role.ability_id', '=', 'abilities.id') ->select('abilities.*') ->distinct(); // 避免同一权限被多个角色重复返回 }
这样你直接调用auth()->user()->abilities就能拿到当前用户的所有权限了,而且是一次数据库查询,完全避免了N+1问题!
方案二:直接用查询构建器连表查询
如果你不想在模型里定义关联,直接用DB查询构建器写也是很清晰的:
$userAbilities = Ability::query() ->select('abilities.*') ->distinct() ->join('ability_role', 'abilities.id', '=', 'ability_role.ability_id') ->join('roles', 'ability_role.role_id', '=', 'roles.id') ->join('role_user', 'roles.id', '=', 'role_user.role_id') ->where('role_user.user_id', auth()->id()) ->get();
这个写法和方案一本质是一样的,都是通过多表连接直接从数据库获取去重后的权限,性能同样优秀。
额外优化:缓存权限列表
如果你的权限不会频繁变动,建议把用户的权限列表缓存起来,避免每次请求都查数据库,比如在User模型里加个缓存方法:
public function getCachedAbilities() { return Cache::remember('user_abilities_' . $this->id, 3600, function () { // 这里可以用pluck只拿权限名称,节省内存 return $this->abilities()->pluck('ability_name')->toArray(); }); }
之后用auth()->user()->getCachedAbilities()就能快速拿到缓存的权限数组了,性能更上一层楼!
为什么不用嵌套foreach?
嵌套foreach会触发N+1查询问题:先查用户的所有角色(1次查询),然后每个角色都要查一次对应的权限(N次查询,N是用户角色数),当用户角色多的时候,性能会直线下降,而上面的方案都是一次查询搞定,显然更优。
而且你提到要动态修改角色的权限,这些方案完全适配——只要数据库里的ability_role关联表更新了,查询结果会自动同步,不需要额外处理。
备注:内容来源于stack exchange,提问作者Telexx
相关产品推荐
相关产品推荐

