本文主要全面讲解在不适用第三方包的情况下,如何在基于Laravel框架上,研发社交系统ThinkSNS+时,简历一套优雅而不失性价比的用户权限管理体系功能,【内含ThinkSNS真实代码】。
需求场景
就是用户组+权限节点,这个需求 laravel 有很多很好的第三方包实现。下面描述代码不参与缓存机制纯数据库查询,给大家提供一个思路。
下面的代码都是来自于ThinkSNS+,是基于 Laravel 全新开发的 ThinkSNS 社交开源项目,遵循 Apache-2.0 开源协议。欢迎 Star 哦。
数据表设计
其实这一块我个人是参考的 Zizaco/entrust 因为我觉得,大多数情况下,我们要用的角色和权限节点都是真多用户的。数据表设计如下:
可以看到关系如下 user -> role -> ability ,其中关系全部都是多对多关系。一个用户可以拥有多个 role,一个 ability 可以被分配给多个 role 。
链式方法设计
$user->ability('create user'); // 判断是否有 create user 权限。
$user->ability('owner', 'delete user'); // 判断用户是否拥有 owner 用户组,且是否这个组拥有 delete user 权限。
$user->ability(); // 返回一个 Ability 实例。
$user->roles; // 读取用户所拥有的所有用户组。
$user->roles(); // 获取 Builder 实例。
$user->roles('owner'); // 检查用户是否拥有 owner 用户组,拥有返回 model 实例,否则返回 false。
$user->ability()->roles(); // 读取用户所拥有的所有用户组。返回的是一个 集合。可用集合所有方法。
$user->ability()->roles('owner'); // 检查用户是否拥有 owner 用户组,拥有返回 model 实例,否则返回 false。
$user->ability()->all(); // 返回用户拥有的所有权限集合。
$user->ability()->all('create user'); // 检查用户是否拥有 create user 权限,没有返回 false ,有返回 ability 实例。
其中调用 $user->ability()->all() 和 $user->ability()->all() 都是返回的 集合 可以链式调用集合下的所有方法进一步操作。
ability 用户 Trait
<?php
namespace ZhiyiPlusModelsConcerns;
use ZhiyiPlusModelsRole;
use ZhiyiPlusServicesUserAbility;
trait UserHasAbility
{
/**
* Abiliry service instance.
*
* @var \Zhiyi\Plus\Services\UserAbility
*/
protected $ability;
/**
* User ability.
*
* @param array $parameters
* ability();
* ability($ability);
* ability($role, $ability);
* @return mixed
* @author Seven Du <shiweidu@outlook.com>
*/
public function ability(...$parameters)
{
if (isset($parameters[1])) {
return ($role = $this->resolveAbility()->roels($parameters[0]))
? $role->ability($parameters[1])
: false;
} elseif (isset($parameters[0])) {
return $this->resolveAbility()
->all($parameters[0]);
}
return $this->resolveAbility();
}
/**
* The user all roles.
*
* @param string $role
* @return mied
* @author Seven Du <shiweidu@outlook.com>
*/
public function roles(string $role = '')
{
if ($role) {
return $this->ability()->roles($role);
}
return $this->belongsToMany(Role::class, 'role_user', 'user_id', 'role_id');
}
/**
* Resolve ability service.
*
* @return \Zhiyi\Plus\Services\UserAbility
* @author Seven Du <shiweidu@outlook.com>
*/
protected function resolveAbility()
{
if (! ($this->ability instanceof UserAbility)) {
$this->ability = new UserAbility();
}
return $this->ability->setUser($this);
}
}
Ability 实例
<?php
namespace ZhiyiPlusServices;
use IlluminateSupportCollection;
use ZhiyiPlusModelsUser as UserModel;
use ZhiyiPlusContractsModelUserAbility as UserAbilityContract;
class UserAbility implements UserAbilityContract
{
protected $user;
/**
* Get all roles or get first role.
*
* @param string $role
* @return mixed
* @author Seven Du <shiweidu@outlook.com>
*/
public function roles(string $role = '')
{
$roles = $this->user()
->roles()
->get()
->keyBy('name');
if (! $role) {
return $roles;
}
return $roles->get($role, false);
}
/**
* Get all abilities or get first ability.
*
* @param string $ability
* @return mixed
* @author Seven Du <shiweidu@outlook.com>
*/
public function all(string $ability = '')
{
$roles = $this->roles();
$roles->load('abilities');
$abilities = $roles->reduce(function ($collect, $role) {
return $collect->merge(
$role->abilities->keyBy('name')
);
}, new Collection());
if (! $ability) {
return $abilities;
}
return $abilities->get($ability, false);
}
/**
* Get user instance.
*
* @return \Zhiyi\Plus\Models\User
* @author Seven Du <shiweidu@outlook.com>
*/
public function user(): UserModel
{
return $this->user;
}
/**
* Set user model.
*
* @param \Zhiyi\Plus\Models\User $user
* @author Seven Du <shiweidu@outlook.com>
*/
public function setUser(UserModel $user)
{
$this->user = $user;
return $this;
}
}
Role 模型所需代码
<?php
namespace ZhiyiPlusModels;
use IlluminateDatabaseEloquentModel;
class Role extends Model
{
/**
* Get all abilities of the role.
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
* @author Seven Du <shiweidu@outlook.com>
*/
public function abilities()
{
return $this->belongsToMany(Ability::class, 'ability_role', 'role_id', 'ability_id');
}
/**
* Get or check The role ability.
*
* @param string $ability
* @return false|\User\Plus\Models\Ability
* @author Seven Du <shiweidu@outlook.com>
*/
public function ability(string $ability)
{
return $this->abilities->keyBy('name')->get($ability, false);
}
}
使用
然后我们打开 User 模型wen jia文件添加如下代码:
class User ...
{
use UserHasAbility;
}
总结
其实性状在 User 模型中只暴露了 roles 和 ability 两个公开方法。但是已经足以胜任用户组权限判断逻辑了。
整个 ability 都是结合在集合之上的一些封装,这样是的代码调用更加优雅。
以上代码是在开发ThinkSNS+中的实际真实代码。具体的实现可参考项目。
以上代码都来自于ThinkSNS Plus,看完整的开发代码可以看仓库:
GitHub: https://github.com/slimkit/thinksns-plus
(开源不易,求 Star )
ThinkSNS产品免费体验:http://www.thinksns.com/experience.html