> ## 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 透過 request 物件的 `validate` 方法或專用的 form request 類別，提供簡潔而強大的驗證功能。

```mermaid theme={null}
flowchart TD
    A["收到 HTTP 請求"] --> B["套用驗證規則"]
    B --> C{{"驗證是否<br>通過？"}}
    C -->|"成功"| D["取得驗證通過的資料"]
    D --> E["執行 controller 邏輯"]
    E --> F["成功回應"]
    C -->|"失敗<br>(Web 請求)"| G["導向前一頁<br>將錯誤存到 session"]
    C -->|"失敗<br>(API 請求)"| H["422 Unprocessable Entity<br>JSON 錯誤回應"]
```

## 於 Controller 進行驗證

### `$request->validate()` 的使用

在 controller 方法內呼叫 `$request->validate()` 是最簡單的驗證方式。
驗證失敗時，Laravel 會自動將使用者導回前一頁，並把錯誤資訊存到 session。

定義路由：

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

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

建立 controller：

```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` 方法傳入以欄位名為 key、規則為 value 的陣列。
規則可用 pipe `|` 分隔，或以陣列指定。

```php theme={null}
// 以 pipe 分隔
'title' => 'required|string|max:255',

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

### 主要驗證規則

| 規則           | 說明                              |
| ------------ | ------------------------------- |
| `required`   | 值必須存在且非空                        |
| `string`     | 必須為字串                           |
| `email`      | 必須為有效 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 會自動讓所有 view 都能使用 `$errors` 變數。
這由 `web` middleware 群組中的 `ShareErrorsFromSession` middleware 負責。

### 顯示所有錯誤

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

### 依欄位顯示錯誤

用 `@error` 指示詞可為特定欄位在 inline 顯示錯誤。

```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>

## Form Request

### 什麼是 Form Request

當驗證邏輯變複雜時，將其拆出到「form request」類別會更有效。
form request 是把驗證與授權邏輯整合在一起的自訂 request 類別。

```mermaid theme={null}
flowchart TD
    A["呼叫 controller 方法"] --> 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["執行 controller 方法"]
```

### 建立 Form Request

以 `make:request` Artisan 指令產生 form request 類別。

```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()` 方法

以陣列回傳驗證規則。格式與傳給 controller `validate` 方法的陣列相同。

### `authorize()` 方法

判斷是否有權執行此請求。
回傳 `true` 表示授權通過，回傳 `false` 會自動回傳 403 回應。

只允許已認證使用者時，可用 `auth()->check()` 確認。
教學中回傳 `true` 即可。

<Warning>
  若 `authorize` 方法回傳 `false`，controller 方法不會執行，會回傳 403 Forbidden 回應。
</Warning>

### 注入到 Controller

Form request 只要在 controller 方法上以 type-hint 指定即可使用。
呼叫 controller 前會先執行驗證，因此方法內無需寫驗證碼。

```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="建立 form request">
    ```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="Controller 的實作">
    ```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 token，Laravel 會回傳 419 錯誤。
</Info>

## 後續步驟

<Card title="HTTP Request" icon="arrow-up-from-bracket" href="/zh-TW/requests">
  複習如何用 request 物件取得資料。
</Card>


## Related topics

- [Laravel MCP](/zh-TW/mcp.md)
- [Laravel Prompts](/zh-TW/prompts.md)
- [HTTP 測試](/zh-TW/http-tests.md)
- [驗證（Verify）](/zh-TW/packages/laravel-bluesky/verify.md)
- [自訂驗證規則](/zh-TW/advanced/custom-validation-rules.md)
