概要
先日こちらの記事でLaravel Authorization Policyを使った管理画面におけるリソースアクセス制限について書きましたが、シンプルな権限管理であればモデルのイベントフックの方がよっぽどシンプルに実装できたのでメモ。Backend.Behaviors.FormControllerを実装しているコントローラを対象とします。
やり方
モデルイベントをフック
下記の例ではProductというモデルデータがロードされたタイミング(model.afterFetchイベント)で
Product::extend(function ($model) use ($user) {
$model->bindEvent('model.afterFetch', function () use ($model, $user) {
if (/* $modelと$userをつかって権限チェックをするロジックをここに記述 */) {
// アクセス権がないときにtrueになるようにして、アクセス権なしだと例外を投げる
throw new NoAccessRightToResourceException("アクセス権がありません");
}
});
});
ドキュメントではモデルのイベントフック登録は基本的に Plugin::boot() に記述すると書かれているが、結局は使用される前に登録されていればよい。本記事の場合、管理画面でのみ使用したいので、管理画面のコントローラのコンストラクタに記述すれば十分間に合う。
そして、権限がなかった時の処理を例外ハンドラとして登録する。こちらも同様にコンストラクタに記述するので十分。
App::error(function (NoAccessRightToResourceException $exception) {
// アクセス権がない時の処理
Flash::error($exception->getMessage());
});
基本的にはこれだけなのだが、これだけだと例外が上記で登録した例外ハンドラまで届かない場合がある。ここが落とし穴。
\Backend\Behaviors\FormController::update を見ると catch (Exception $ex) で全ての例外がキャッチされて$this->controller->handleError($ex); でハンドルされてしまっている。つまり、このままだと上記のようにApp::errorで登録したハンドラまで例外が届かない。なので、このhandleErrorメソッドをオーバーライドしてハンドラに登録した例外の場合はthrowし直すようにする。
public function handleError($exception){
if ($exception instanceof NoAccessRightToResourceException) {
// 例外を投げ直してApp::errorで登録したハンドラでハンドルさせる
throw $exception;
} else {
parent::handleError($exception);
}
}
以上