> ## 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 的校验功能来验证表单输入数据。

## 什么是校验

校验（Validation）指的是验证用户提交的数据是否符合预期的格式与条件。
Laravel 通过请求对象的 `validate` 方法，以及专用的 Form Request 类，提供了简洁而强大的校验能力。

```mermaid theme={null}
flowchart TD
    A["接收到 HTTP 请求"] --> B["应用校验规则"]
    B --> C{{"校验是否<br>通过？"}}
    C -->|"成功"| D["取得校验后的数据"]
    D --> E["执行控制器逻辑"]
    E --> F["成功响应"]
    C -->|"失败<br>(Web 请求)"| G["重定向回前一页<br>错误存入 Session"]
    C -->|"失败<br>(API 请求)"| H["422 Unprocessable Entity<br>返回 JSON 错误"]
```

## 在控制器中做校验

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

在控制器方法中调用 `$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']);
```

编写控制器：

```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',
]);

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>

## Form Request

### 什么是 Form Request

当校验逻辑较复杂时，可将其抽离到「Form Request」类中。
Form Request 将校验与授权逻辑集中到一个自定义请求类中。

```mermaid theme={null}
flowchart TD
    A["调用控制器方法"] --> B["生成 FormRequest 实例"]
    B --> C["执行 authorize()"]
    C --> D{{"授权通过？"}}
    D -->|"false"| E["403 Forbidden 响应"]
    D -->|"true"| F["执行 rules()"]
    F --> G["运行校验"]
    G --> H{{"校验通过？"}}
    H -->|"失败"| I["校验错误响应"]
    H -->|"成功"| J["执行控制器方法"]
```

### 创建 Form Request

用 `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;
    }

    /**
     * 返回应用到此请求的校验规则
     */
    public function rules(): array
    {
        return [
            'title' => 'required|string|max:255',
            'body'  => 'required|string',
            'email' => 'required|email|unique:posts',
        ];
    }
}
```

### `rules()` 方法

以数组形式返回校验规则，与直接传给 `validate` 的一致。

### `authorize()` 方法

判断是否有权限执行该请求。
返回 `true` 通过，返回 `false` 时自动返回 403。

若只允许已认证用户，可用 `auth()->check()`。
教程中直接返回 `true` 即可。

<Warning>
  当 `authorize` 返回 `false` 时，控制器方法不会执行，会直接返回 403 Forbidden。
</Warning>

### 注入到控制器

只需在控制器方法上做类型提示即可使用 Form Request。
控制器执行前会先运行校验，因此无需在方法内再写校验代码。

```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="实现控制器">
    ```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` 指令，否则 Laravel 会返回 419 错误。
</Info>

## 下一步

<Card title="HTTP 请求" icon="arrow-up-from-bracket" href="/zh/requests">
  回顾使用请求对象获取数据的方式。
</Card>


## Related topics

- [Laravel Prompts](/zh/prompts.md)
- [Laravel MCP](/zh/mcp.md)
- [HTTP 测试](/zh/http-tests.md)
- [校验 (Verify)](/zh/packages/laravel-bluesky/verify.md)
- [CSRF 保护](/zh/csrf.md)
