独自ポリシーの導入手順と注意点(ハマリポイント)を解説

はじめに

ユーザーと記事を関連付けておいて、編集や削除は記事の作成者のみ許可したいときにはポリシーを作成します。今回はポリシーの導入手順と注意点(ハマリポイント)を解説します。

導入手順

ポリシー作成

ターミナルで以下のコマンドを実行します。

$ php artisan make:policy ArticlePolicy
Policy created successfully.

作成されたapp/Policies/ArticlePolicy.phpを以下のように設定します。

<?php

namespace App\Policies;

use App\User;
use App\Article;

class ArticlePolicy
{
    public function view(User $user, Article $article)
    {
        return $user->id === $article->user_id;
    }
}

view関数を定義し、ポリシーの真偽値を返り値に設定します。今回はログインユーザーと記事の作成者が一致しているかを判定しています。

ポリシーとモデルの関連付け

作成したポリシーとモデルを関連付けます。app/Providers/AuthServiceProvider.phpを以下のように設定します。

<?php

namespace App\Providers;

use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Gate;
use App\Article;
use App\Policies\ArticlePolicy;

class AuthServiceProvider extends ServiceProvider
{
    /**
     * The policy mappings for the application.
     *
     * @var array
     */
    protected $policies = [
        Article::class => ArticlePolicy::class,
    ];
}

Articleモデルを操作するときはArticlePolicyポリシーを適用させるように設定しています。

ポリシーの適用

コントローラーにポリシーを適用します。今回は記事の作成者のみ編集と削除を許可したいので、editupdatedestroyにポリシーを適用させます。

app/Http/Controllers/ArticlesController.phpを以下のように設定します。

    public function __construct()
    {
        $this->middleware('can:view,article', ['only' => ['edit', 'update', 'destroy']]);
    }

★ハマリポイント1
middleware関数の第1引数ですが、'can:view, article'のようにカンマの後にスペースを入れるとポリシーにうまく引数が渡せないので注意してください。

うまくいかない場合

ポリシーの作成から導入まで行ったにもかかわらず、実際に動かしてみると、記事の作成者にもかかわらず編集や削除ができないことがあります。記事の作成者でhttp://localhost:8000/articles/1/editにリクエストしても、403ページにリダイレクトされてしまう状況です。

★ハマリポイント2
結論から言うと、DBから取得した値のデータ型が一致していないのが原因です。

app/Policies/ArticlePolicy.phpをもう一度見てみます。

    public function view(User $user, Article $article)
    {
        return $user->id === $article->user_id;
    }

===比較演算子を使っています。これは、両辺の値および型が一致している場合のみtrueとなる比較演算子です。値は一致しているはずなので、型が一致していないのではないかと推測できます。

実際にDBから取得した型がどうなっているか調べてみます。

$ php artisan tinker
>>> gettype(App\User::first()->id);
=> "integer"
>>> gettype(App\Article::first()->user_id);
=> "string"

Articleモデルのuser_idの型がstringになっています。なぜこのような型変換が起こるのかというと、PHPのMySQLドライバの設定が関連しているようなのですが、要はPHPの仕様ということのようです。

型変換が起こって型が不一致となっていることはわかりました。これを解消するために、モデルのcastsプロパティを設定します。

app/Article.phpを以下のように設定します。

class Article extends Model
{
    protected $casts = [
        'user_id' => 'integer',
    ];
}

これでuser_idintegerで取得してくれるようになります。

$ php artisan tinker
>>> gettype(App\Article::first()->user_id);
=> "integer"

integerで取得できてますね。

まとめ

記事中のハマリポイントは、実際に私がハマったところです😅ハマリポイント2に関しては、Laravelの開発チームもPHPの仕様を理解した上で、castsプロパティを作ったのではないかと思います。

関連記事

開発環境(ローカルストレージ)と本番環境(Amazon S3)にトリミング+リサイズした画像を保存する方法
# はじめに 以前、以下の記事を書きました。 - [開発環境と本番環境で画像のアップロード先を分岐する方法 \- AUTOVICE](https://www.autovice.jp/articles/68) - [ローカルやAmazon [...]
2020年3月7日 11:56
Laravel Mixの仕組みをわかりやすく解説:CSSをSASS/SCSSで記述する方法
# はじめに Laravel Mixの仕組みをわかりやすく解説します。Laravel Mixを使ってアセットコンパイルを行うことで、SASS/SCSSでCSSを記述できるようになります。 # Laravel Mixの仕組み まずはじめに、 [...]
2020年3月6日 9:25
【Laravel 6.x】Laravelプロジェクトの名称を変更する方法
# はじめに あまりないケースではありますが、一度作成したLaravelプロジェクトの名称を変更する方法を解説します。ここで言うLaravelプロジェクトの名称とは、以下のコマンドの`Sample`のことを指します。 ```terminal [...]
2020年3月4日 16:24
【Laravel】ローカルディスクやAmazon S3にアップロードした画像をビューで表示する方法
# はじめに 本記事では、ローカルディスクやAmazon S3にアップロードした画像をビューで表示する方法について説明しています。 なお、本記事では画像のアップロードする方法については説明していません。本記事は既に実装済みの画像アップロード [...]
2020年3月4日 15:44
【Laravel】Amazon S3へのアップロードが「403 Access Denied」で失敗する原因と対処方法
# はじめに LaravelでAmazon S3へ画像アップロードする処理を実装し、いざアップロードしようとしたところ、以下のエラーが出て失敗しました。 ``` Error executing "PutObject" on "https: [...]
2020年3月4日 13:33
開発環境と本番環境で画像のアップロード先を分岐する方法
# はじめに この記事では、以下の方針で画像のアップロード先を分岐する方法について解説しています。 - 開発環境:ローカル(storage/app/public/) - 本番環境:Amazon S3 # 事前準備 AWSに未登録の [...]
2020年3月4日 12:52
ユーザー認証機能の導入手順と生成されたフォームを日本語化する方法を解説
# はじめに Laravelではユーザー認証機能の導入が簡単にできてしまいます。この記事ではLaravelプロジェクトにユーザー認証機能を追加する手順と、生成されたフォーム(デフォルトでは英語)を日本語化する方法を解説します。 # 前提 [...]
2020年2月29日 13:54
【Laravel 6.x】ネスト構造になっているRESTfulルーティングの定義方法
# はじめに RESTfulルーティングを定義するときは`Route::resource`を使いますが、ネスト構造になっているRESTfulルーティングの定義方法を調べたので記事にします。 # 前提 おそらく、Laravel 6.xのみ対 [...]
2020年2月28日 20:06