Laravel Filamentで請求システム作る No5 権限機能を追加
前回に引き続きFilamentで請求システムを作っていきます。
ユーザー管理まで作ったら、やっぱり権限機能を作りたくなってきました。
別に僕一人で利用するシステムなので、必要ないっちゃないのですが、
今後Filamentで案件をこなす時は確実に必要な機能なので、
このタイミングを逃すのはもったいない。
LaraveとFilamentでの権限について
laravelにはgateとpolicyという権限機能が有ります。
こちらの記事がものすごく解りやすかったので、読んでみてください。要は、laravelとしてはgateとpolicyで画面やデータに対する権限を作ってね。
って事みたいですね。
そしてfilamentもplicyで自動的にリソースへのアクセスを自動で制御できるようになっているみたいです。
要は、laravelのpolicyを上手い事つくれたら、
後はfilamentが勝手にやってくれるみたいですね。
laravelのpolicyとFilamentリソースでの挙動
viewAny
viewAny
がTrueなら、
サイドバーのメニューにリソース名が表示される。
Falseならそのリソースへはアクセスできない。
view
Trueならば、レコードの表示が可能
Filamentでは、表示画面が表示できるという意味
編集画面が表示できるけど、更新ボタンが無いという意味ではない。
表示画面について
create
Trueならば、新規作成ができる
Filamentでは、作成画面が表示でき、作成ボタンをクリックできるという意味
作成画面について
update
Trueならば、レコードの更新ができる
Filamentでは、作成画面が表示でき、作成ボタンをクリックできるという意味
delete
Trueならば、レコードの削除ができる
forceDelete
複数のレコードを一括削除できる
restore
復元できる
ここからはFilament独自のplicy
正直使いどころを理解できていないので、さらっと行きます。
reorder
並び替えができる
という事で実験
手動でUserモデルに対してPolicyを作ってみる。
Userモデルはusersテーブルを操作するクラスなので、
命名規則はUserPolicy
になる。
という事で、以下のコマンドを実行する
./vendor/bin/sail artisan make:policy UserPolicy
でけた。
たとえば、
UserPolicy
のviewAny
関数の返り値がfalse
の場合は
サイドバーに表示されないそうなので、そんなソースを書いてみた。
namespace App\Policies;
use App\Models\User;
class UserPolicy
{
public function viewAny()
{
return false;
}
}
お~!表示しない!
Trueだと表示する
public function viewAny()
{
return false;
}
同じように、createとviewとupdateとdeleteも全部Falseにした。
class UserPolicy
{
public function viewAny()
{
return true;
}
public function create()
{
return false;
}
public function view()
{
return false;
}
public function update()
{
return false;
}
public function delete()
{
return false;
}
}
するとこんな感じで一覧だけ表示してその他の操作は全部できなくなった。
URLを直接叩いてみる。
updateがtrueならば、http://localhost/admin/users/1/edit
で編集画面が表示するはずだけど、
updateがfalseならば、ちゃんと403が帰ってくる。
これをDBで管理しちゃえばいいんだね。
DB構造を考える
ちょっと調べたけど、policyをDBで管理する方法を検討してる記事が無かったので、
考えてみる。
- user_has_policysテーブルを用意する
- user_has_policysでは、ユーザーID・モデル名(英語)・viewAny・view・create・update・deleteのカラムが有る。
- usersから見たuser_has_policysはhasManyの関係
- UserModelにhasPolicyという関数を作り、モデル名(英語)と権限名(viewAnyという文字列)を送ると良い感じにBooleanを返してくれる。
- 4の関数をpolicyで利用するようにする。
画面は別で考える
じゃあとりあえず、データとDBだけ作って行こう。
マイグレーションとモデルを作る
./vendor/bin/sail artisan make:model UserHasPolicy -m
でuser_has_policyのモデルとマイグレーションを一気に作る
マイグレーションファイルを作る
public function up(): void
{
Schema::create('user_has_policies', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id');
$table->string('model_name', 50)->comment('モデル名');
$table->boolean('view_any')->comment('一覧表示が可能か');
$table->boolean('create')->comment('作成が可能か');
$table->boolean('update')->comment('更新が可能か');
$table->boolean('delete')->comment('削除が可能か');
$table->boolean('view')->comment('詳細表示が可能か');
$table->timestamps();
});
}
とりあえずこんな感じ。
マイグレーションを実行する
./vendor/bin/sail artisan migrate
テーブルができたので、適当にデータを登録する。
laravelのboolean型はmysqlではtinyintになるので、0or1で登録する。
モデルを作る
UserHasPolicyモデル
fillableを書いて。
マイグレーションでbooleanとしたカラムをcastして0 or 1からtrue or falseに変換できるようにしておく。
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class UserHasPolicy extends Model
{
use HasFactory;
protected $fillable = [
'user_id',
'model_name',
'view_any',
'create',
'update',
'delete',
'view',
];
protected $casts = [
'view_any' => 'boolean',
'create' => 'boolean',
'update' => 'boolean',
'delete' => 'boolean',
'view' => 'boolean',
];
}
Userモデル
UserHasPolicyとhasManyのリレーションを作るり、
hasPolicyというメソッドを作る
use Illuminate\Database\Eloquent\SoftDeletes;
+ use App\Models\UserHasPolicy;
class User extends Authenticatable
{
//省略
'email_verified_at' => 'datetime',
'password' => 'hashed',
];
+ public function hasPolicy($modelName, $policyName)
+ {
+ $userHasPolicy = UserHasPolicy::where('user_id', $this->id)
+ ->where('model_name', $modelName)
+ ->first();
+ if ($userHasPolicy) {
+ return $userHasPolicy->$policyName;
+ }
+ return false;
+ }
+ public function userHasPolicy()
+ {
+ return $this->hasMany(UserHasPolicy::class);
+ }
}
policyで利用する
namespace App\Policies;
use App\Models\User;
class UserPolicy
{
+ private $modelName = 'user';
- public function viewAny()
+ public function viewAny(User $user)
{
- return true;
+ return $user->hasPolicy($this->modelName, 'view_any');
}
- public function create()
+ public function create(User $user)
{
- return false;
+ return $user->hasPolicy($this->modelName, 'create');
}
こんな感じにした。
DBの登録状況は
viewAnyを1(True)でその他は0(false)
となって、一覧画面を表示できた。
DBの登録状況は全部1
で全部の処理が可能になった。
次回
DBのレコードを利用したPolicyの動作について理解できたので、
次回はリファクタリングをしながら他のモデルのPolicyも作っていきます。
Discussion