やってみたLaravelチュートリアル④

# はじめに 🐢
[Laravelの教科書](https://amzn.asia/d/1xFOXD5)という本を見かけたので、
勉強のために、チュートリアルとして頭からやってみました。
その個人的な記録です。

本は 350ページ、12章 ありますが、内容的に
2～10章が手を動かしてやる系ぽいです。

環境構築、仕組み、DBと、第２～６章は済ませました。

- [やってみたLaravelチュートリアル①](https://qiita.com/tmatsumor/items/2cc8f9ac49a6fa28ad37)

- [やってみたLaravelチュートリアル②](https://qiita.com/tmatsumor/items/d2f1495ad1e2e8c099a1)

- [やってみたLaravelチュートリアル③](https://qiita.com/tmatsumor/items/1b07d49652bdb9333f2a)

次の第７章、第８章に進みます。

:::note info
本が手元にあることが前提となっています
:::

# Ⅶ．一覧表示
第７章では、Laravel での一覧表示について解説してあります。
モデル上でのテーブル結合が出てきます。

## １．一覧ページの作成
### ルート
教科書に従い、一覧ページ用のルート設定を作ります。
get メソッドにて、PostController の index を呼び出します。

::: note info
アドレスとしては localhost/post となります。
:::
### コントローラ
以前作成した PostController に index メソッドを追加します。

::: note info
::all() や compact については [こちらを参照](https://qiita.com/tmatsumor/items/1b07d49652bdb9333f2a#testcontrollerphp)。
:::
### ビュー
受け取った配列を foreach で回して画面に出します。
まずは、create.blade.php を複製して、index.blade.php を作ります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3508336/b03ec0da-1542-081f-c378-f460cdb290f9.png)
教科書に従い、ビュー内部に @foreach を書き込みます。
以下のように、投稿が縦方向に列挙されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3508336/4220b7cd-8c59-3585-81a5-82de93c45ee6.png)

:::note warn
ログアウト状態だと、以下のエラーが表示されます。
Attempt to read property "name" on null
ログインして試しましょう。
:::

## ２．リレーションの設定
### リレーション
Post モデルと User モデルの間にリレーションを張ります。
一人の User が複数の Post を持ち得ます。
### マイグレーション
紐づけのために、posts テーブルに user_id 列を追加します。
教科書のコマンドを実行し、マイグレーションファイルを作ります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3508336/16a3ae97-3683-eda3-5154-81ac3225bd44.png)

up と down にコードを書き込み、migrate します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3508336/8257b1f1-7a63-5339-e0a2-ba0569b27982.png)
コントローラとモデルを修正し、投稿時に user_id に値が入るようにします。
PostController.php と Post.php が対象です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3508336/2277ab8c-1091-0f9e-ee9e-85c0e84d4b6b.png)

::: note info
posts テーブルの従来の user_id 列が 0 になっているので
後々のトラブルを避けるために UPDATE 文で
全て同じ user_id を割り当てておきましょう（ここでは 1）。
:::

### モデルへのリレーション設定
テーブルに列を準備したら、次はリレーションを設定していきます。
User モデルでは posts() にて hasMany メソッドを、
Post モデルでは user() にてbelongsTo メソッドを呼び出します。
User ： Post ＝ 1 ： 多 です。

### ビューからの参照

教科書に従い、index.blade.php に追記します。
$post の user を参照し、そのプロパティの name を取得します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3508336/c3747d8e-72f5-5bb9-49fd-9fef2ee44bb1.png)
投稿に紐づいたユーザー名を表示することができました。

::: note warn
エラー Attempt to read property "name" on null が出る場合は、
posts テーブルの user_id 列に 0 が入っていることが原因です。

:::

## ３．絞り込み

ここでは、Eloquent ORM を用いたデータの絞り込みと
並び替えについて記述してあります。

教科書に従い、PostController.php の index メソッドにて
Post::all(); の代わりに、user_id で絞り込む where 句を用意します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3508336/7e95a023-3c5b-28bc-fc03-10063d8eb9c6.png)
DB 側で user_id を書き換えて、localhost/post を開くと
一覧表示から特定の user_id の投稿が非表示となりました。

適切に user_id にて絞り込みが行われているようです。

ほか、各種の where 句、order by 句 での並び替え、
first や find などの単一行取得について解説があります。

### Eager ロード
単純に、リレーションを張って取得するだけだと、
[N+1問題](https://qiita.com/muroya2355/items/d4eecbe722a8ddb2568b) にてパフォーマンスが下がる事があるそうです。

Laravel では、その解決として with メソッドによる
Eager ロードを提供しています。

Post::all(); の代わりに、Post::with('user')->get(); にて
あらかじめ Post モデルを通じた posts テーブルの読み込み時に
users テーブルも併せて読み込んで、Laravel 側で結合します。

ループ中など、高頻度のDBアクセスが予想される場合は
Eager ロードを積極的に使用する必要があります。

# Ⅷ．ミドルウェア

ミドルウェアとはコントローラの前後に挟まる処理です。
ルーターの後に前処理ミドルウェアが入り、
コントローラを挟み、
レスポンスの前に後処理ミドルウェアが入ります。

## 1．ミドルウェアの設定箇所

Kernel.php にミドルウェアのパスを書きます。
その中には、以下の３つがあります。
- グローバルミドルウェア
- ミドルウェアグループ
- ミドルウェアエイリアス

グローバル（$middleware）は全体に効いているミドルウェア、
グループは routes/web.php のように、ルートごとに有効なもの、
エイリアスは必要な時に個別で呼び出して用いるミドルウェアです。

それぞれ事例が教科書に書かれていますので、試してみて適切に動作するか確認します。

## 2．ミドルウェアの自作
割愛します。また必要になったら読み返します。

## ３．Gate による制限

>　ゲートは、ユーザーが特定のアクションを実行することを
>　許可されているかどうかを判断する単なるクロージャです。

https://readouble.com/laravel/8.x/ja/authorization.html

Gate は AppServiceProvider.php にて設定します。
教科書に従い、use 宣言部と Gate の定義部を追記します。Gate 名は test です。

### ルートでのアクセス制御
routes/web.php ファイルの /（トップページ：localhost）に middleware にて
can:test を指定します。
Gate 定義部の指定通り、ログインユーザーのIDが 1 の場合のみ、通過できます。

それ以外のユーザーの場合は、403 が表示されることを確認します。
![スクリーンショット 2023-10-18 0.03.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3508336/7a230f2a-c968-acd2-6f5c-2554584836f4.png)

::: note info
ユーザー名のドロップダウンよりログアウトし、Register から新しいユーザーを作ります。
:::

::: note warn
localhost に Gate を設定するので Register に到達できない場合があります。
その場合は、一旦 web.php の ->middleware(... の記述を除去して下さい。
:::


### ビューでの表示制御
ビューでも Gate が使えます。
welcome.blade.php にて @can ... @endcan にてコードを追記します。
ゲートが機能し、条件を満たした時のみ @can 部が出力される事を確認します。

### コントローラでの動作制御
コントローラ にて 動作制御を行います。
PostController.php にて Gate::authorize を追記し、条件を満たさない場合 403 とします。
教科書では投稿時に呼び出される store にて認可制御を呼び出しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3508336/c459f80d-8890-3de1-71c4-0cbf090f6428.png)

::: note info
教科書では auth と admin ミドルウェアの削除が指示されていますが、
本記事ではミドルウェアの自作の節を割愛しているので、上の二つのミドルウェアは
web.phpに含まれません。よって web.php は触りません。
:::

# まとめ
以上、第７，８章でした。
リレーション、絞り込み、ミドルウェアと Laravel の
便利な機能の中核となるような内容でした。

まだまだ触りではありますが、雰囲気やFW設計の意図を感じる事はできたかなと思います。
入門では、この二章が峠かと思いました。

次は、９章以降、お楽しみの CRUD と、ペジネーションなどの便利機能です。
次回が最後になります！

# 参考
- [N+1問題がなんもわからんというだけの記事](https://zenn.dev/kinzal/articles/c5745e7d9a950c)
- [[解説] SQLクエリのN+1問題](https://qiita.com/muroya2355/items/d4eecbe722a8ddb2568b)
- [Laravelのwith関数（Eagerロード）でクエリチューニング](https://cloudsmith.co.jp/blog/backend/laravel/2020/07/1506704.html)
- [Laravelで認可(Gate)](https://qiita.com/toontoon/items/f6206760d62ee2e66c5f)
- [Laravel認可における GateとPolicyの扱いについて](https://qiita.com/TK-CLIT/items/f48dbd968c7e42d6378b)