輕量化的Laravel群組/權限管理套件-spatie/laravel-permission

輕量化的Laravel群組/權限管理套件-spatie/laravel-permission

spatie/laravel-permission是一個非常輕量化權限管理的套件,他實際上是利用User-Role-Permission的架構。Role可以當成群組的概念來使用。

當初會知道這個套件的另一個原因是看到Laravel News上的推薦。另一個原因是因為他有持續在更新(這也是Laravel News推薦的原因!)。實際使用上也非常的好上手+好理解。以下會就筆者有使用到的操作做簡單介紹:

安裝

透過composer安裝十分方便。

composer require spatie/laravel-permission

接著一樣在config/app.phpproviders加入

'providers' => [
// ...
Spatie\Permission\PermissionServiceProvider::class,
];

publish migration

php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider" --tag="migrations"

在做migration前我們可以先看一下新增的migration檔案:database\migrations\TIMESTAMP_create_permission_table ,會發現他新增了5個表,分別是

表名稱 欄位名稱
roles id, name
permissions id, name
user_has_roles role_id, user_id
user_has_permissions permission_id, user_id
role_has_permissions permission_id, role_id

這部份應該滿簡單的,就是user-role-permission的對應關係。

其中比較要注意的就是對應關係的Table預設上是用外鍵處理(foreign key),所以當你要使用Role::truncate()的時候要另外特別處理。

另外需要注意的是會使用到原本new Laravel 時候就有的User table喔!不過如果沒有的話也沒關係後面可以自己創,並把關係對應起來即可!

稍微了解一下之後就可以下migrate了!

php artisan migrate

然後把config檔匯出

php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider" --tag="config"

然後你就可以在config/permission.php 內看到config file:

return [
    'models' => [
        //...
        'permission' => Spatie\Permission\Models\Permission::class,
        //...
        'role' => Spatie\Permission\Models\Role::class,
    ],
    'table_names' => [
        //...
        'roles' => 'roles',
        //...
        'permissions' => 'permissions',
        //...
        'model_has_permissions' => 'model_has_permissions',
        //...
        'model_has_roles' => 'model_has_roles',
        //...
        'role_has_permissions' => 'role_has_permissions',
    ],
    //...
];

簡單的說就是對應表單及名字的設定,若像上面說的你有更動table的話,這邊也要跟著做設定喔!

設定

使用的部份會依筆者本身開發使用的習慣做講解,不過因為架構真的滿簡單的,相信大家都很容易理解XD

首先需要在User Model 內加入Trait Spatie\Permission\Traits\HasRoles,才可以使用到套件提供的功能。

use Illuminate\Foundation\Auth\User as Authenticatable;
use Spatie\Permission\Traits\HasRoles;
class User extends Authenticatable
{
    use HasRoles;
    // ...
}

因為筆者有用到User 做Auth, 如果沒有的朋友可以直接extend model是沒關係的。

一般來說筆者在使用這種MVC框架時,會在Model 和Controller 層中間加入一個Repository層,去處理一些邏輯的問題,相關說明可以參照點燈坊大大的介紹。在Repository注入時,記得要使用的是套件提供的Model!同理因為套件提供的 Model,所以他有直接使用 HasPermissions的Trait。如果你設定的表名稱和預設不同,可能需要另外加入Trait。

namespace App\Repositories;
use Spatie\Permission\Models\Role;
class RoleRepository
{
    private $roles;
    public function __construct(Role $roles)
    {
        this->roles = roles;
    }

    //...
}
//
namespace App\Repositories;
use Spatie\Permission\Models\Permission;
class PermissionRepository
{
    private $permission;
    public function __construct(Permission $permission)
    {
        $this->permission = permission;
    }
}

在注入 Model的同時,其實可以順便看一下Model 和 Trait 裡面的code,其實也十分簡單易懂,像是在role model 內對 User 及 Permission 都是多對多的概念

public function roles()
{
    return $this->belongsToMany(
    config('laravel-permission.models.role'),
    config('laravel-permission.table_names.role_has_permissions')
    );
}

public function users()
{
    return $this->belongsToMany(
    config('auth.model') ?: config('auth.providers.users.model'),
    config('laravel-permission.table_names.user_has_permissions')
    );
}

使用

一般如果我們用的是user-role-permission架構的話,常用的功能如下:

這邊強調一下,傳入的值,一般是用’name’的欄位喔!若你有趣查找程式碼可以發現他會用app(Permission::class)->findByName($permissions);去搜尋名稱,所以記得不要傳入 ID!不過倒是可以直接傳入collection。

$user->assignRole('writer'); //新增user 的 role
$user->removeRole('writer'); //移除user 的 role
$user->syncRoles(params); //更新user 及 role 的 對應
$role->givePermissionTo('edit articles');//新增role 的 permission
$role->revokePermissionTo('edit articles');//移除role 的 permission
$role->syncPermissions(params);//更新role 及 permission 的 對應

會建議用以上六個function做角色/權限設定,是因為當使用以上功能時,會自動的更新Permission的cache,這是筆者在開發時遇到的一個坑!若你有查找程式碼的話會發現他們都有實作$this->forgetCachedPermissions();的方法。所以若你只是在資料庫處理資料時,會導致其實你的表單已經被 Cache過,你的變更無法套用到環境中,而要另外執行更新 Cache的功能。

如果你只是做資料庫的操作的話,記得要使用更新Cache的指令:

php artisan cache:forget spatie.permission.cache

當然官網提供了十分詳盡的其他功能解說,想要使用其他的功能可以直接在官網查詢。

實例

筆者在使用時,通常會把控管權限的的位置放在 Request內。

例如我有一個User 的 Controller,要管控有權限的人才能進入index, 就會在IndexRequest內做管控。

public function index(\App\Http\Requests\Settings\User\IndexRequest $request)
{
    return view('backend/settings/users/index');
}

//在IndexRequest內使用authorize
class IndexRequest
{
    public function authorize()
    {
        $user = Auth::user();
        return $user->hasPermissionTo('pemissionName');
    }
    public function rules()
    {
        return [
        //...your validation
        ];
    }
    public function attributes()
    {
        return [
        //...your attributes
        ];
    }
}

這邊因為筆者有實作Auth,所以Auth::user()的方式去取得登入的User 資料(Model),詳閱。接著可以用$user->hasPermissionTo(‘pemissionName’)去確認。

註:若去查看程式碼,你會發現hasPermissionTo(‘permissionName’)實際上是會返回$this->hasDirectPermission($permission) or $this->hasPermissionViaRole($permission);兩個方法,所以這邊可以不用特地轉換到Role Model去查找Permission。

而其他的像是編輯儲存的動作也是用Request下去做控管。當然你要建立一個若是失敗就要跳轉的頁面。

總結

簡單來說,只要照個官網的步驟來做安裝,然後在你要使用的地方加入 HasRolesHasPermissions的Trait就可以安心的利用他提供的功能,套件內的程式碼也相對的好懂,個人十分推薦Laravel的初學者(像我)使用的。

當然這邊的教學只描述了一小部分筆者有使用的部份,還有相當多的東西官網都有寫但是筆者都沒有用,像是在view層的實現Unit Testing等等,相當推薦大家去使用!

另外要提一下幾個我覺得可以改進的地方:

  1. 預設沒有提供群集命名的功能:在開發平台時一定會遇到像是基礎設定底下在分為幾個權限,這種有關連的名稱在預設的情況下並無提供,所以在分類上會比較麻煩,不過應該可以用手刻的方式來處理,還是等有心人士要丟個PR…

  2. 筆者覺得官網的描述排版可以修正一下,像遇到的資料庫快取問題要看到最後才發現,應該要在一開始的時候就提出才對,然後盡量讓大家使用那六個方法實作。