Laravel5.1.11で追加されたGateを試してみる(その1)

# 概要
Laravel5.1.11でユーザー権限管理がうまくまとめられるGateがオプションで追加されたみたいなのでまずは試してみる。
該当箇所の公式ドキュメントはこちら
[Authorization(公式英語)](http://laravel.com/docs/5.1/authorization)

公式ドキュメントと軽くソースを見た感じでは以下の感じかなと理解してみた。
1. routes.phpと似た要領で`AuthServiceProvider`にポリシーを定義できる。
2. 定義したポリシーをGateファサードを使って権限等のチェックが必要な場所で利用する。
3. Policyを使うときはUserモデルがxxxxに対して操作するポリシーをxxxxPolicyに定義するという感じ
4. ログインしていない時(Userがnull)は指定ポリシーに到達する前にfalseが返される？

オブジェクト毎の括りでPolicyファイルを分けることで
ポリシーのコードが散らばらずにまとめることができ、権限チェックロジックは`app/Policies`見ればいいっていう単純な状態にしやすい所でしょうか

# どんな用途に使うか
管理画面等でよくある閲覧のみ～追加編集可能等のRoleから
フロントエンドでも自分の書き込んだコメントのみ編集可能というのでも使いやすそう。

特に権限チェックが複雑になりがちな箇所や、
同じコンテンツに対してCRUDできる人が違う場合など一つのPolicyクラスにまとめやすい分メリットを感じられそうです。
権限チェックの部分が完全に分離できるだけも、テストしやすくなるので意味はあるかと思いました。
ってことで、まずはよくあるパターンを用意して使ってみる。

# 環境と開始前の状態
`composer create-project`でLaravel5.1.12が入った状態。
今回の機能は5.1.11で追加されたものですが、bugfixですぐ5.1.12になった模様。

## 5.1.11へアップグレードするには
公式のアップグレードガイドを見ると早い。(英語)
http://laravel.com/docs/5.1/upgrade

# 今回試すサンプルの要件
ざっと作ったよくあるケースのサンプルを使って、以下の要件で利用してみる。

1. 登録ユーザーがログインすることで「記事」を投稿できる。
2. 自分が投稿した「記事」は編集が可能。
3. 自分が投稿した「記事」は削除が可能。
4. 登録ユーザーは「記事」対して「コメント」を書き込める。
5. 自分が投稿した「コメント」は編集が可能。
6. 自分が投稿した「コメント」は削除が可能。
7. 自分の「記事」に投稿された他ユーザーの「コメント」は削除可能。
8. 投稿された「記事」と「コメント」は非ログインユーザーでも閲覧は可能。
9. 管理者権限を持ったユーザーは全投稿に対して全操作が可能

ファイル名は以下の意味で作る。
User = ユーザー
Post = 記事
Comment = コメント

# まずPost(記事)から作る
まずは記事に関する部分から権限を追加する。

## PostPolicyを作る
以下のコマンドでartisanを使って雛形が作れが、できるのはコンストラクタがあるのみ。
`php artisan make:policy PostPolicy`
ファイルは`app/Policies`に作られる。

生成された雛形に、「記事」に対する要件を満たすための以下のmethodを追加する
> 1. 登録ユーザーがログインすることで「記事」を投稿できる。
> 2. 自分が投稿した「記事」は編集が可能。
> 3. 自分が投稿した「記事」は削除が可能。

[実際のコードサンプル](https://github.com/inaka-phper/laravel-role/commit/15b50a270ecbf91805cca9704c60601a111055da)

```PostPolicy.php
    /**
     * 1. 登録ユーザーがログインすることで「記事」を投稿できる。
     * @return bool
     */
    public function create()
    {
        return true;
    }

    /**
     * 2. 自分が投稿した「記事」は編集が可能。
     * @param User $user
     * @param Post $post
     * @return bool
     */
    public function update(User $user, Post $post)
    {
        return $user->id == $post->user_id;
    }

    /**
     * 3. 自分が投稿した「記事」は削除が可能。
     * @param User $user
     * @param Post $post
     * @return bool
     */
    public function delete(User $user, Post $post)
    {
        return $user->id == $post->user_id;
    }
    
```

## PostPolicyをAuthServiceProvider設定する
`AuthServiceProvider`に作ったPolicyを設定する。
Policyクラスを作った場合、`$policies`に追記してあげる。
keyは対象のオブジェクトクラス名で設定する必要がある。

[実際のコードサンプル](https://github.com/inaka-phper/laravel-role/commit/ff69c85b9ddbe2111785d7b5855e20f3ca342b9e)

```AuthServiceProvider.php
    protected $policies = [
        'App\Model' => 'App\Policies\ModelPolicy',
        Post::class => PostPolicy::class,
    ];
```

## PostPolicyを使う
今回はどんな感じに使うのか試すだけなので、シンプルにコントローラーで行う。
`denies`で対象として渡したクラス名読み取り、
先ほどの`AuthServiceProvider`の`policies`の配列キーに設定されている`Policy`を自動で読んでくれる。
こんな感じで各所に追記。

[実際のコードサンプル](https://github.com/inaka-phper/laravel-role/commit/f12f564a90ee8d4af413a97752b5c03450820c29)

```PostController.php
        if (Gate::denies('update', $this->post)) {
            return redirect('/post')->with('message', '編集できるのは投稿者と管理者のみです。');
        }
```

## 権限の状態によって記事の編集・削除ボタンの表示を制御する。
権限がないユーザーに編集・削除ボタンを表示しても邪魔でしかないので、
編集・削除ボタンを権限に合わせて出来るものだけ表示するようにする。
実現方法もちゃんと用意されているのでありがたく使用する。

[実際のサンプルコード](https://github.com/inaka-phper/laravel-role/commit/4ffdfdb4cf6d93461836480ca10f8552665768cb)

```index.blade.php
    @can('update', $post)
    <a href="/post/{{ $post->id }}/edit" class="btn btn-primary">
        <span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> 編集
    </a>
    @endcan
    @can('delete', $post)
    <button class="btn btn-danger" data-toggle="modal" data-target="#exampleModal" data-whatever="/post/{{ $post->id }}">
        <span class="glyphicon glyphicon-trash" aria-hidden="true"></span> 削除
    </button>
    @endcan

```

一緒に追加された`@can`を使うとphp側で使うような方法で使える。

スクショもとってみた。
「エンジニア」でログインしているので、「phper」の記事には編集削除ボタンが表示されない。

![ブログ一覧.png](https://qiita-image-store.s3.amazonaws.com/0/85113/cf6529b3-0fe7-3a71-7f3f-bd06acd402a7.png)


# 長くなったのでここで区切る

~~次回はコメント部分の実装例で書いてみる。~~
書いた。
Laravel5.1.11で追加されたGateを試してみる(その2)
http://qiita.com/inaka_phper/items/c584904bb39673e2dc16
その2では、複数の値をPolicyに渡すときの方法がメインです。


追々`AuthServiceProvider`への定義方法や、`$gate->before()`と`Gate::allows`も試してサンプル作ってみたいと思います。