Laravel 他人のデータは閲覧、編集、削除できない、などをサクッと実現する一つのやり方
まえがき
よく会員システムなどで、他人のデータは閲覧、編集、削除できない、などの処理が必要になる事があります。そんな時、各メソッドであれこれ書いたり、又はLaravelの認可(ポリシー)機能などを使ったりするのが一般的だと思います。
ただ案件によっては、各メソッドであれこれ書きたくない…、とか、ポリシー作る程でもないし…、サクッと仕上げたい、なんて事もあるかと思います。
ということで、そんな時に使えるかも知れない方法について見て行きます。
(あくまで、選択肢の1つとして見て行くというレベルです)
その前に
そもそも各メソッドでやる場合は、どんな風に書いたりするかと言うと、下記みたいな感じです。
/**
* 変更画面
*/
public function edit(Post $post, Request $request)
{
if ($request->user()->isNot($post->user)) {
// 他人のデータは見られません、とエラーにする
}
ここでの $post は、ルートモデル結合を使っています。
では、サクッとやる場合は、どんな風になるかと言うと、コントローラーのコンストラクタ内で、ミドルウェアを定義して、次のような感じにします。(各メソッドでは、ルートモデル結合を使っているのが前提です)
public function __construct()
{
$this->middleware(function ($request, $next) {
$post = $request->route('post');
if ($request->user()->isNot($post->user)) {
abort(403);
}
return $next($request);
})->except(['xxx', 'yyy']);
}
middleware(callback) の形で、そのコントローラに適用するミドルウェアを定義します。
最後の ->except(['xxx', 'yyy']); は、適用除外するメソッドを指定しています。
もちろん、->only('edit', '...'); などとして、逆に適用するメソッドを列挙することもできます。
上記はもう少し行を節約して、次のように書けますね。
public function __construct()
{
$this->middleware(function ($request, $next) {
abort_if($request->user()->isNot($request->route('post')->user), 403);
return $next($request);
})->except(['xxx', 'yyy']);
}
以上です。
まぁ、行数的にはサクッと感は微妙ですが、1箇所で管理できるというメリットはあるかと思います。
話は逸れますが…
今の時代、下記みたいな書き方をする人はいませんが、下記のように書いた場合、
protected $user;
public function __construct()
{
$this->user = auth()->user();
}
この場合、認証済みユーザー情報を取得する事はできません。
何故かと言うと、要は、Laravel が呼び出すミドルウェアの情報を収集する為に、コントローラーのコンストラクターを呼び出しますが、その時点ではまだ、そもそもルートミドルウェアが走っていないから(=セッションなどのミドルウェアが走ってないから)という事になります。
ついでに言ってしまうと、
public function __construct()
{
$post = request()->route('post');
}
ルートモデル結合だから上記で Post モデルを取得しようとしても、意図した通りには動作せず、
単に連番の数字のみが取得されます。これは、ルートモデル結合もミドルウェアで実現されているからです。
(昔の記事ではありますが…)
参考:Laravel 5.3 コントローラのコンストラクタの重要な変更
参考:Controller Construct Session Changes in Laravel 5.3
参考:Getting the current user (or other session data) in a Laravel controller constructor
参考:GitHub An Open Letter to the Core Laravel Developer Team...
雑感
どのように実装するにしても、テストは必ず作っておいた方がいいですね。
誤りなどありましたらコメント下さい。
Discussion