Laravelのエラーハンドリングとバリデーションを含んだAPIについて

## はじめに
この記事はLaravelでAPIを操作する際のエラーハンドリングとバリデーションに関する手順をまとめたメモになります

## ルーティングの設定
`routes/api.php` に、APIのルートを追加することで、基本的なPOSTリクエストを受け取るAPIを設定します。
```php
Route::post('/user', [UserController::class, 'store']);
```

## コントローラーの作成
以下のコマンドでコントローラーを生成することができます
```php
php artisan make:controller UserController
```

## コントローラーに必要なメソッド（例）

```UserController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class UserController extends Controller
{
    public function store(Request $request)
    {
        // バリデーション処理
        $validated = $request->validate([
            'name' => 'required|string|max:255',
            'email' => 'required|email|max:255|unique:users',
            'password' => 'required|min:8',
        ]);

        // 正常処理
        return response()->json(['message' => 'User created successfully']);
    }
}

```

## マスアサインメントの設定

### マスアサインメントとは

特定の作業やリソース（人、物、情報など）を一度に多くの対象に割り当てることを指します。
例えば、学校のイベントで多くの生徒が役割を持つ場合、一斉に役割を振り分けることが`マスアサインメント`です。

### Laravelのマスアサインメント
この記事では一度に複数のデータを連想配列としてモデルに渡し、その全てを`一括で保存すること`をマスアサインメントと呼称しております
Laravelではセキュリティのために、マスアサインメントで保存する際には制限が設けられており、この制限によってマスアサインメントが許可されていないフィールドに対して`一括でデータを保存しようとする`と、システムはマスアサインメントによる不正な操作と判断し、エラーを発生させます

### マスアサインメントの例
以下の例では保存処理で 'name', 'email', 'password'を新規保存する際にバリデーションの対象となっていたので`マスアサインメントの設定`を実施しています
```User.php


<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    // マスアサインメントを許可するフィールドを指定
    protected $fillable = ['name', 'email', 'password'];
}

```

## バリデーションの方法と概要

バリデーションとは、入力されたデータが正しいかどうかを確認することです。
例えば、ウェブサイトのフォームに情報を入力するときに、その情報が適切かどうかをチェックするために使われます。これにより、間違ったデータや不完全な情報が送信されるのを防ぎます。



### バリデーションの例



```***controller.php
$validated = $request->validate([
            'name' => 'required|string|max:255',
           
            'email' => 'required|email|max:255|unique:users',
            'password' => 'required|min:8',
        ]);
```
- name フィールドに対して`必須` `文字列` `最大255文字`という3つのルールを適用する場合は上記のように書きます
- リクエストの中身が`required`, `string`, `max:255` の3つのルールを満たさない場合はエラーを発生させます
#### メソッドの補足
- $request->validate()

リクエストから送信されたデータ（$request）に対してバリデーションを行います。データが正しくない場合は、自動的にエラーレスポンスが返されます

- $request

クライアントから送信されたデータを含むオブジェクトです

- validate() 

リクエストのデータがルールに従っているかどうかを検証します

## バリデーションの種類について

Laravelで設定できるバリデーションルール以下の通り様々存在します
### 必須/オプション
- accepted: フィールドが yes, on, 1, または true であることを確認
- active_url: フィールドが有効なURLであることを確認
- after:date : フィールドが指定された日付より後の日付であることを確認
- after_or_equal:date : フィールドが指定された日付以上であることを確認
- alpha: フィールドがアルファベットのみであることを確認
- alpha_dash: アルファベット、数字、ダッシュ、アンダースコアのみであることを確認
- alpha_num: アルファベットと数字のみであることを確認
- array: フィールドが配列であることを確認
- bail: 最初のバリデーションエラーが発生したら、後続のルールをスキップ

### 数値/長さ
- between:min,max: 数値、文字列、またはファイルのサイズが指定された範囲内であることを確認
- boolean: フィールドが true または false であることを確認
- confirmed: 指定されたフィールド（通常はパスワード）が確認フィールドと一致することを確認
- date: フィールドが有効な日付であることを確認
- date_equals:date : 指定された日付と等しいことを確認
- date_format:format: フィールドが指定されたフォーマットと一致することを確認

### ファイル
file: フィールドがアップロードされたファイルであることを確認
image: 画像ファイルであることを確認（jpeg, png, bmp, gif, svg, webp）
mimes:types: ファイルが指定されたMIMEタイプ（例: jpeg, png）であることを確認
mimetypes:types: ファイルが指定されたMIMEタイプ（例: image/jpeg, video/mp4）であることを確認

### 比較
- different:field: 他のフィールドと異なることを確認
- digits:value: 指定された桁数であることを確認
- digits_between:min,max: 指定された範囲内の桁数であることを確認
- email: フィールドが有効なメールアドレス形式であることを確認
- exists:table,column: データベースの指定されたテーブルとカラムに値が存在することを確認

### ユニーク性
- unique:table,column: 指定されたテーブルのカラムにおいて値がユニークであることを確認

### サイズ/範囲
- max:value : フィールドが指定された最大サイズ（文字数やファイルサイズなど）以下であることを確認
- min:value : フィールドが指定された最小サイズ以上であることを確認

### その他
- nullable: フィールドが空でも許容することを指定
- regex:pattern: フィールドが指定された正規表現パターンに一致することを確認
- required: フィールドが必須であることを確認
- required_if:anotherfield,value: 指定されたフィールドが特定の値の場合に、このフィールドが必須であることを確認
- required_with:field1,field2,...: 指定された他のフィールドが存在する場合、このフィールドが必須であることを確認

## Laravelのエラーハンドリング

バリデーションなどによってエラーが発生した場合にエラーを適切に処理するための仕組みをエラーハンドリングと呼びます。
エラーハンドリングをすることで、ユーザーに対して理解しやすいエラーメッセージを表示したり、ログにエラー情報を記録したりすることができます。

### エラーハンドリングの流れ
バリデーションによるエラーなどが発生した場合、以下の流れでエラー処理が実行されます

#### ① エラーオブジェクトがスローされる
- 「スロー」するとは「投げる」という意味合いで、例えばバリデーションエラーの場合が発生した場合は`controller`で`ValidationException`というエラーオブジェクトが生成されます。そのオブジェクトはLaravelのフレームワークによって自動的にキャッチされ、`Handler.php`に渡されます
- ValidationExceptionはバリデーションルールに従わない入力があった場合にcontrollerで作成されるエラー情報が格納されたオブジェクトです
- ログインをしていないユーザーがログイン後のページを見ようとした時に発生する`AuthorizationException`などエラーの種類によってエラーオブジェクトは複数存在し、Handler.phpへスローされます


#### ② Handler.phpの呼び出し
Handler.phpではエラーが発生した際の処理全般が定義されており、生成されたエラーオブジェクトをIF文などで判断し実行される処理を区別できます。

#### ③ renderメソッドの実行
Handler.phpにはrenderメソッドが存在し、スローされたエラーオブジェクトが引数として渡されます。このメソッド内では、エラーオブジェクトが$exceptionという変数に代入され、if文などを使ってエラーオブジェクトの種類に応じた特定の処理（例えば、エラーページの表示や適切なレスポンスの生成）を行うことができます。


## Handler.phpについて

Laravelのアプリケーション内でエラーや例外が発生した場合、上述の通り`app/Exceptions/Handler.php` が呼び出されその情報は `$exception` という変数に格納されます
```Handler.php
namespace App\Exceptions;

use Exception;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;

class Handler extends ExceptionHandler
{
    // エラーを記録する
    public function report(Throwable $exception)
    {
        // エラーの情報を記録します
        parent::report($exception);
    }

    // エラーメッセージを返す
    public function render($request, Throwable $exception)
    {
        // 特定のエラーに対してカスタムメッセージを返す
        if ($exception instanceof CustomException) {
            return response()->json(['error' => 'カスタムエラーメッセージ'], 400);
        }

        // デフォルトのエラーメッセージを返す
        return parent::render($request, $exception);
    }
}

```
- `parent::report($exception);`
Laravelがエラーや問題が起こったときに、そのエラーの情報を自動で記録してくれる機能を呼び出すためのものです。削除した場合はLaravelの標準のエラーログ（storage/logs/laravel.log に保存）は行われません。

- `if ($exception instanceof CustomException)`
$exception という変数に入っているエラーが、CustomException という特定のエラー（例外）であるかどうかを確認する」ためのコードです。
- `return response()->json()`
response() は、Laravelのヘルパー関数で、HTTPレスポンス（サーバーからクライアントへの応答）を作成するためのものです。json() は、レスポンスをJSON形式に変換します。APIでは、データをやり取りするときにJSON形式がよく使われます
- `json(['error' => 'カスタムエラーメッセージ'], 400)`
'error' というキーに対して 'カスタムエラーメッセージ' という内容が設定されています。このメッセージはGoogle Chromeの検証ツールからレスポンスなどで確認できます。同時に`HTTPステータスコード（400）`が返されます。HTTPステータスの番号およびメッセージの内容は自由に変更できます




### エラーページに誘導させる

エラーが発生した際に特定のエラーページへ自動でリダイレクトさせたい場合は、以下のようにエラーページへリダイレクトさせます
```Handler.php
public function render($request, Throwable $exception)
{
    // 401エラー（未認証）
    if ($exception instanceof \Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException) {
        return redirect('/401'); // 401ページにリダイレクト
    }

    // 403エラー（アクセス拒否）
    if ($exception instanceof \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException) {
        return redirect('/403'); // 403ページにリダイレクト
    }

    // 404エラー（ページが見つからない）
    if ($exception instanceof \Symfony\Component\HttpKernel\Exception\NotFoundHttpException) {
        return redirect('/404'); // 404ページにリダイレクト
    }

    // 500エラー（サーバー内部のエラー）
    if ($exception instanceof \Symfony\Component\HttpKernel\Exception\HttpException) {
        return redirect('/500'); // 500ページにリダイレクト
    }

    // その他のエラーについては、標準のエラーメッセージおよびページを表示
    return parent::render($request, $exception);
}

```

#### HTTPステータスについての補足（エラーコード）
- 401エラー
認証が必要なページにアクセスした際にリダイレクトされます
- 403エラー
アクセスが拒否された場合、403エラーページにリダイレクトされます
- 404エラー
指定されたページが見つからない場合、404エラーページにリダイレクトされます
- 500エラー
サーバーで何らかの問題が発生した場合、500エラーページにリダイレクトされます
- その他のエラー
上記の条件に該当しないエラーが発生した場合、Laravelのデフォルトのエラーハンドリングが行われます。