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

# 概要
Laravel5.1.11で追加されたGateを試してみる(その1)の続き。
http://qiita.com/inaka_phper/items/653820a75d1a9c716f0e

今回はコメント部分の以下の要件を満たすために、
Policyを使って実装する部分についてサンプルを交えて描いていきます。
4~6,8はPostと一緒になりますが、7が渡すオブジェクトとロジックが増える感じですね。

4. 登録ユーザーは「記事」対して「コメント」を書き込める。
5. 自分が投稿した「コメント」は編集が可能。
6. 自分が投稿した「コメント」は削除が可能。
7. 自分の「記事」に投稿された他ユーザーの「コメント」は削除可能。
8. 投稿された「記事」と「コメント」は非ログインユーザーでも閲覧は可能。


# コメント部分を作る
基本はPostと一緒なので、Postと違う動きをする部分
「7. 自分の「記事」に投稿された他ユーザーの「コメント」は削除可能。」を実装するdelete部分をメインに記載。


## CommentPolicyのdeleteを作る
「7. 自分の「記事」に投稿された他ユーザーの「コメント」は削除可能。」の「自分の記事」を確認するため
該当Postも一緒に受け取るようにする。
受け取る値を増やしたいときは、純粋に引数増やすだけでいいらしい。

CommentからPostを引っ張ってくることも可能ですが、Policyの中でSQL発行しちゃうと多方面から呼ばれる事を考えると
無駄なSQLが増えてさらに依存度が高くなってしまうので、必要なオブジェクトを受け取る実装がいいんじゃないかと思う。

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

```CommentPolicy.php
    /**
     * 6. 自分が投稿した「コメント」は削除が可能。
     * 7. 投稿された「記事」と「コメント」は非ログインユーザーでも閲覧は可能。
     * @param User $user
     * @param Post $post
     * @param Comment $comment
     * @return bool
     */
    public function delete(User $user, Comment $comment, Post $post)
    {
        return $user->id == $post->user_id || $user->id == $comment->user_id;
    }
```

## CommentPolicyをAuthServiceProvider設定する
Postに同じくAuthServiceProvider.phpへ書き足す。

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

## CommentPolicyのdelete(User, Comment, Post)を使う
deleteでは引数が増えたので、追加で`Post`のオブジェクトを渡してあげる必要がある。
**Policyで引数増やしたから、渡すの増やせばいいんじゃと見せかけて配列で渡さないといけない作り。**
**配列の順番にも注意が必要で、最初に来るのがPolicyを読み込むキーになるので注意が必要。**
順番間違うと間違えたほうのdeleteでチェックが走ってしまう。

今回は`CommentPolicy`なので`Comment`を最初に渡す。

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

```PostCommentController.php
        if (Gate::denies('delete', [$this->comment, $this->post])) {
            return redirect('/post/' . $this->post->id)->with('message', '削除できるのは投稿者と記事の投稿者、管理者のみです。');
        }
```

## 権限の状態によって表示を制御する。
引数を増やしたCommentPolicyのdeleteを利用する際は
Controllerに記述したように以下のように配列で渡す。
こちらも順番に注意が必要。

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

```show.blade.php
    @can('delete', [$comment, $post])
        <button class="btn btn-danger" data-toggle="modal" data-target="#exampleModal" data-whatever="/post/{{ $post->id }}/comment/{{ $comment->id }}">
            <span class="glyphicon glyphicon-trash" aria-hidden="true"></span> 削除
        </button>
    @endcan
```

コメント投稿フォームは、ログインしないと入力できないようにしたいので
`@can`でも使える`@else`を利用して出しわける

```show.blade.php
    @can('create', $post)
        <form class="form-horizontal" role="form" method="POST" action="{{ url('/post/' . $post->id . '/comment') }}">
            <input type="hidden" name="_token" value="{{ csrf_token() }}">
            <input type="hidden" name="_method" value="POST">

            <div class="form-group">
                <div class="col-md-5">
                    <textarea name="content" class="form-control">{{ old('content') }}</textarea>
                </div>
                <div class="col-md-2">
                    <button type="submit" class="btn btn-primary">
                        投稿
                    </button>
                </div>
            </div>

        </form>
    @else
        <p>コメントを投稿するには <a href="/auth/login">ログイン</a>してください。</p>
    @endcan

```

結果、画面上はこんな感じ。
![記事.png](https://qiita-image-store.s3.amazonaws.com/0/85113/9f2743a6-9122-21a2-7540-ae0d9a5ce94c.png)


# ここでも一旦区切り

次回はbeforeを利用した管理者だからなんでもOKな実装方法を試してみます。
Laravel5.1.11で追加されたGateを試してみる(その3)
http://qiita.com/inaka_phper/items/09e730bf5a0abeb9e51a