需求場景
就是使用者組+許可權節點,這個需求 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()->roles()
和 $user->ability()->all()
都是返回的 集合 可以鏈式呼叫集合下的所有方法進一步操作。
ability 使用者 Trait
<?php
namespace Zhiyi\Plus\Models\Concerns;
use Zhiyi\Plus\Models\Role;
use Zhiyi\Plus\Services\UserAbility;
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 Zhiyi\Plus\Services;
use Illuminate\Support\Collection;
use Zhiyi\Plus\Models\User as UserModel;
use Zhiyi\Plus\Contracts\Model\UserAbility 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 Zhiyi\Plus\Models;
use Illuminate\Database\Eloquent\Model;
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 模型檔案新增如下程式碼:
class User ...
{
use UserHasAbility;
}
總結
其實性狀在 User 模型中只暴露了 roles
和 ability
兩個公開方法。但是已經足以勝任使用者組許可權判斷邏輯了。
整個 ability 都是結合在集合之上的一些封裝,這樣是的程式碼呼叫更加優雅。
以上程式碼是在開發 ThinkSNS+ 中的實際真實程式碼。具體的實現可參考專案。
最後,開源不易,大家可以看下 ThinkSNS+ 程式,覺得不錯幫忙點一個 Star。?
本作品採用《CC 協議》,轉載必須註明作者和本文連結
Seven 的程式碼太渣,歡迎關注我的新擴充包 medz/cors 解決 PHP 專案程式設定跨域需求。