> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# バリデーション

> Laravelのバリデーション機能を使って、フォームの入力データを検証する方法を解説します。

## バリデーションとは

バリデーションとは、ユーザーから送信されたデータが期待する形式や条件を満たしているかを検証することです。
Laravelはリクエストオブジェクトの `validate` メソッドや専用のフォームリクエストクラスを使った、シンプルで強力なバリデーション機能を提供しています。

```mermaid theme={null}
flowchart TD
    A["HTTPリクエスト受信"] --> B["バリデーションルール適用"]
    B --> C{{"バリデーション<br>通過？"}}
    C -->|"成功"| D["バリデーション済みデータ取得"]
    D --> E["コントローラーロジック実行"]
    E --> F["成功レスポンス"]
    C -->|"失敗<br>(Webリクエスト)"| G["前の画面にリダイレクト<br>エラーをセッションに保存"]
    C -->|"失敗<br>(APIリクエスト)"| H["422 Unprocessable Entity<br>JSONエラーレスポンス"]
```

## コントローラーでのバリデーション

### `$request->validate()` の使い方

コントローラーのメソッド内で `$request->validate()` を呼び出すのが、最もシンプルなバリデーション方法です。
バリデーションに失敗すると、Laravelはユーザーを前の画面に自動でリダイレクトし、エラー情報をセッションに保存します。

ルートを定義します。

```php theme={null}
use App\Http\Controllers\PostController;

Route::get('/post/create', [PostController::class, 'create']);
Route::post('/post', [PostController::class, 'store']);
```

コントローラーを作成します。

```php theme={null}
<?php

namespace App\Http\Controllers;

use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\View\View;

class PostController extends Controller
{
    public function create(): View
    {
        return view('post.create');
    }

    public function store(Request $request): RedirectResponse
    {
        $validated = $request->validate([
            'title' => 'required|string|max:255',
            'body'  => 'required|string',
            'email' => 'required|email',
        ]);

        // バリデーション済みのデータで投稿を保存する...

        return redirect('/posts');
    }
}
```

`validate` メソッドにはフィールド名をキー、ルールを値とした配列を渡します。
ルールはパイプ `|` で区切るか、配列で指定できます。

```php theme={null}
// パイプ区切り
'title' => 'required|string|max:255',

// 配列形式
'title' => ['required', 'string', 'max:255'],
```

### 主要なバリデーションルール

| ルール           | 説明                                  |
| ------------- | ----------------------------------- |
| `required`    | 値が存在し、空でないこと                        |
| `string`      | 文字列であること                            |
| `email`       | 有効なメールアドレス形式であること                   |
| `min:値`       | 文字列の長さ、または数値が指定値以上であること             |
| `max:値`       | 文字列の長さ、または数値が指定値以下であること             |
| `unique:テーブル` | 指定したテーブルで値が一意であること                  |
| `nullable`    | 値が `null` でも許可する                    |
| `integer`     | 整数であること                             |
| `boolean`     | 真偽値（`true`/`false`、`1`/`0` など）であること |
| `date`        | 有効な日付形式であること                        |
| `confirmed`   | `フィールド名_confirmation` フィールドと一致すること  |

<Info>
  利用可能なすべてのバリデーションルールは[公式ドキュメント](https://laravel.com/docs/validation#available-validation-rules)で確認できます。
</Info>

### バリデーション済みデータの利用

`validate` メソッドの戻り値は、バリデーションに通ったデータのみを含む配列です。
信頼できるデータとして安全に使えます。

```php theme={null}
$validated = $request->validate([
    'title' => 'required|string|max:255',
    'body'  => 'required|string',
]);

// $validated は ['title' => '...', 'body' => '...'] の形式
Post::create($validated);
```

## Bladeテンプレートでのエラー表示

バリデーションに失敗すると、Laravelは `$errors` 変数をすべてのビューで自動的に利用できるようにします。
`web` ミドルウェアグループに含まれる `ShareErrorsFromSession` ミドルウェアがこれを担っています。

### すべてのエラーを一覧表示する

```blade theme={null}
@if ($errors->any())
    <ul>
        @foreach ($errors->all() as $error)
            <li>{{ $error }}</li>
        @endforeach
    </ul>
@endif
```

### フィールドごとにエラーを表示する

`@error` ディレクティブを使うと、特定のフィールドのエラーをインラインで表示できます。

```blade theme={null}
<label for="title">タイトル</label>

<input
    id="title"
    type="text"
    name="title"
    value="{{ old('title') }}"
    class="@error('title') is-invalid @enderror"
/>

@error('title')
    <div class="error-message">{{ $message }}</div>
@enderror
```

<Tip>
  `old('フィールド名')` を使うと、バリデーション失敗後もユーザーが入力した値をフォームに再表示できます。
</Tip>

## フォームリクエスト

### フォームリクエストとは

バリデーションロジックが複雑になってきた場合は、「フォームリクエスト」クラスに切り出すのが効果的です。
フォームリクエストはバリデーションと認可のロジックをひとつのクラスにまとめたカスタムリクエストクラスです。

```mermaid theme={null}
flowchart TD
    A["コントローラーメソッド呼び出し"] --> B["FormRequestのインスタンス生成"]
    B --> C["authorize()メソッド実行"]
    C --> D{{"認可OK？"}}
    D -->|"false"| E["403 Forbiddenレスポンス"]
    D -->|"true"| F["rules()メソッド実行"]
    F --> G["バリデーション実行"]
    G --> H{{"バリデーション<br>通過？"}}
    H -->|"失敗"| I["バリデーション<br>エラーレスポンス"]
    H -->|"成功"| J["コントローラーメソッド実行"]
```

### フォームリクエストの作成

`make:request` Artisanコマンドでフォームリクエストクラスを生成します。

```shell theme={null}
php artisan make:request StorePostRequest
```

`app/Http/Requests/StorePostRequest.php` が生成されます。

```php theme={null}
<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class StorePostRequest extends FormRequest
{
    /**
     * このリクエストを実行する権限があるか判定する
     */
    public function authorize(): bool
    {
        return true;
    }

    /**
     * このリクエストに適用するバリデーションルールを返す
     *
     * @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
     */
    public function rules(): array
    {
        return [
            'title' => 'required|string|max:255',
            'body'  => 'required|string',
            'email' => 'required|email|unique:posts',
        ];
    }
}
```

### `rules()` メソッド

バリデーションルールを配列で返します。コントローラーの `validate` メソッドに渡す配列と同じ形式です。

### `authorize()` メソッド

このリクエストを実行する権限があるかを判定します。
`true` を返すと認可OK、`false` を返すと403レスポンスが自動的に返されます。

認証済みユーザーのみ許可する場合、`auth()->check()` で確認できます。
チュートリアルでは `true` を返しておけば問題ありません。

<Warning>
  `authorize` メソッドが `false` を返すと、コントローラーのメソッドは実行されず、403 Forbiddenレスポンスが返されます。
</Warning>

### コントローラーへの注入

フォームリクエストはコントローラーメソッドのタイプヒントとして指定するだけで使えます。
コントローラーが呼び出される前にバリデーションが実行されるため、メソッド内にバリデーションコードを書く必要はありません。

```php theme={null}
use App\Http\Requests\StorePostRequest;

class PostController extends Controller
{
    public function store(StorePostRequest $request): RedirectResponse
    {
        // ここに到達した時点でバリデーション済み

        $validated = $request->validated();

        Post::create($validated);

        return redirect('/posts');
    }
}
```

`$request->validated()` でバリデーション済みのデータのみを取得できます。

## 実践例：投稿フォームの作成と保存

フォームの表示から送信・バリデーション・保存までの一連のフローです。

<Steps>
  <Step title="ルートの定義">
    ```php theme={null}
    use App\Http\Controllers\PostController;

    Route::get('/posts/create', [PostController::class, 'create']);
    Route::post('/posts', [PostController::class, 'store']);
    ```
  </Step>

  <Step title="フォームリクエストの作成">
    ```shell theme={null}
    php artisan make:request StorePostRequest
    ```

    ```php theme={null}
    public function rules(): array
    {
        return [
            'title' => 'required|string|max:255',
            'body'  => 'required|string',
        ];
    }

    public function authorize(): bool
    {
        return true;
    }
    ```
  </Step>

  <Step title="コントローラーの実装">
    ```php theme={null}
    use App\Http\Requests\StorePostRequest;
    use App\Models\Post;

    class PostController extends Controller
    {
        public function create(): View
        {
            return view('posts.create');
        }

        public function store(StorePostRequest $request): RedirectResponse
        {
            Post::create($request->validated());

            return redirect('/posts');
        }
    }
    ```
  </Step>

  <Step title="Bladeテンプレートの作成">
    ```blade theme={null}
    {{-- resources/views/posts/create.blade.php --}}

    <h1>新しい投稿</h1>

    @if ($errors->any())
        <ul>
            @foreach ($errors->all() as $error)
                <li>{{ $error }}</li>
            @endforeach
        </ul>
    @endif

    <form method="POST" action="/posts">
        @csrf

        <div>
            <label for="title">タイトル</label>
            <input
                id="title"
                type="text"
                name="title"
                value="{{ old('title') }}"
            />
            @error('title')
                <span>{{ $message }}</span>
            @enderror
        </div>

        <div>
            <label for="body">本文</label>
            <textarea id="body" name="body">{{ old('body') }}</textarea>
            @error('body')
                <span>{{ $message }}</span>
            @enderror
        </div>

        <button type="submit">投稿する</button>
    </form>
    ```
  </Step>
</Steps>

<Info>
  Bladeフォームには必ず `@csrf` ディレクティブを含めてください。CSRFトークンがないとLaravelは419エラーを返します。
</Info>

## 次のステップ

<Card title="HTTPリクエスト" icon="arrow-up-from-bracket" href="/jp/requests">
  リクエストオブジェクトを使ったデータ取得方法を振り返ります。
</Card>
