🙌

Laravel 他人のデータは閲覧、編集、削除できない、などをサクッと実現する一つのやり方

2021/06/09に公開

まえがき

よく会員システムなどで、他人のデータは閲覧、編集、削除できない、などの処理が必要になる事があります。そんな時、各メソッドであれこれ書いたり、又は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