表单请求 #

一、表单请求概述 #

1.1 什么是表单请求 #

表单请求(Form Request)是Laravel提供的一种验证机制,将验证逻辑从控制器分离到独立的请求类中。

text
表单请求优势
├── 关注点分离
│   └── 验证逻辑独立
├── 可复用
│   └── 多个控制器共享
├── 自动验证
│   └── 控制器执行前验证
└── 自动重定向
    └── 验证失败自动返回

1.2 创建表单请求 #

bash
php artisan make:request StoreUserRequest

生成的文件位于 app/Http/Requests/StoreUserRequest.php

二、表单请求结构 #

2.1 基本结构 #

php
namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class StoreUserRequest extends FormRequest
{
    public function authorize()
    {
        return true;
    }

    public function rules()
    {
        return [
            'name' => 'required|string|max:255',
            'email' => 'required|email|unique:users',
        ];
    }
}

2.2 authorize方法 #

用于授权检查,返回 true 表示允许请求继续:

php
public function authorize()
{
    // 只有管理员可以创建用户
    return $this->user()->isAdmin();
    
    // 检查资源所有权
    return $this->user()->can('update', $this->post);
    
    // 始终允许
    return true;
}

2.3 rules方法 #

定义验证规则:

php
public function rules()
{
    return [
        'name' => 'required|string|max:255',
        'email' => 'required|email|unique:users,email',
        'password' => 'required|min:8|confirmed',
        'role' => 'required|in:admin,editor,user',
    ];
}

三、使用表单请求 #

3.1 在控制器中使用 #

php
use App\Http\Requests\StoreUserRequest;

class UserController extends Controller
{
    public function store(StoreUserRequest $request)
    {
        // 验证已通过,可以安全使用数据
        $user = User::create($request->validated());
        
        return redirect()->route('users.show', $user);
    }
}

3.2 获取验证数据 #

php
public function store(StoreUserRequest $request)
{
    // 获取所有验证通过的数据
    $validated = $request->validated();
    
    // 获取部分验证通过的数据
    $partial = $request->safe()->only(['name', 'email']);
    
    // 排除某些字段
    $partial = $request->safe()->except(['password']);
    
    // 获取所有安全数据
    $all = $request->safe()->all();
}

四、自定义错误消息 #

4.1 messages方法 #

php
public function messages()
{
    return [
        'name.required' => '姓名不能为空',
        'email.required' => '邮箱不能为空',
        'email.email' => '邮箱格式不正确',
        'email.unique' => '该邮箱已被注册',
        'password.min' => '密码至少8个字符',
    ];
}

4.2 attributes方法 #

php
public function attributes()
{
    return [
        'name' => '姓名',
        'email' => '邮箱',
        'password' => '密码',
    ];
}

五、条件验证 #

5.1 根据场景区分规则 #

php
public function rules()
{
    $rules = [
        'name' => 'required|string|max:255',
        'email' => 'required|email',
    ];
    
    // 创建时邮箱必须唯一
    if ($this->isMethod('POST')) {
        $rules['email'] .= '|unique:users';
    }
    
    // 更新时排除当前用户
    if ($this->isMethod('PUT') || $this->isMethod('PATCH')) {
        $rules['email'] .= '|unique:users,email,' . $this->user->id;
    }
    
    return $rules;
}

5.2 根据路由参数区分 #

php
public function rules()
{
    $userId = $this->route('user');
    
    return [
        'email' => 'required|email|unique:users,email,' . $userId,
    ];
}

5.3 根据用户角色区分 #

php
public function rules()
{
    $rules = [
        'name' => 'required|string|max:255',
    ];
    
    if ($this->user()->isAdmin()) {
        $rules['role'] = 'required|in:admin,editor,user';
    }
    
    return $rules;
}

六、准备输入 #

6.1 prepareForValidation方法 #

在验证前修改输入数据:

php
protected function prepareForValidation()
{
    // 添加字段
    $this->merge([
        'slug' => Str::slug($this->title),
    ]);
    
    // 格式化字段
    $this->merge([
        'phone' => preg_replace('/[^0-9]/', '', $this->phone),
    ]);
    
    // 转换布尔值
    $this->merge([
        'is_active' => $this->boolean('is_active'),
    ]);
}

6.2 passedValidation方法 #

验证通过后执行:

php
protected function passedValidation()
{
    $this->merge([
        'password' => bcrypt($this->password),
    ]);
}

七、验证后钩子 #

7.1 withValidator方法 #

添加验证后逻辑:

php
public function withValidator($validator)
{
    $validator->after(function ($validator) {
        if ($this->somethingElseInvalid()) {
            $validator->errors()->add('field', '额外验证失败');
        }
    });
}

7.2 自定义验证逻辑 #

php
public function withValidator($validator)
{
    $validator->after(function ($validator) {
        // 检查密码强度
        if (!$this->isStrongPassword($this->password)) {
            $validator->errors()->add(
                'password', 
                '密码必须包含大小写字母、数字和特殊字符'
            );
        }
        
        // 检查唯一组合
        if ($this->hasDuplicateCombination()) {
            $validator->errors()->add(
                'combination', 
                '该组合已存在'
            );
        }
    });
}

八、授权失败处理 #

8.1 自定义授权失败响应 #

php
protected function failedAuthorization()
{
    throw new HttpResponseException(
        response()->json(['message' => '无权执行此操作'], 403)
    );
}

8.2 重定向授权失败 #

php
protected function failedAuthorization()
{
    throw new HttpResponseException(
        redirect()->route('home')->with('error', '无权执行此操作')
    );
}

九、验证失败处理 #

9.1 自定义验证失败响应 #

php
protected function failedValidation(\Illuminate\Contracts\Validation\Validator $validator)
{
    throw new HttpResponseException(
        response()->json([
            'message' => '验证失败',
            'errors' => $validator->errors(),
        ], 422)
    );
}

十、完整示例 #

10.1 创建用户请求 #

php
// app/Http/Requests/StoreUserRequest.php
namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Str;

class StoreUserRequest extends FormRequest
{
    public function authorize()
    {
        return $this->user()->can('create', User::class);
    }

    public function rules()
    {
        return [
            'name' => 'required|string|max:255',
            'email' => 'required|email|unique:users,email',
            'password' => 'required|min:8|confirmed',
            'phone' => 'nullable|regex:/^1[3-9]\d{9}$/',
            'avatar' => 'nullable|image|max:1024',
        ];
    }

    public function messages()
    {
        return [
            'name.required' => '姓名不能为空',
            'email.required' => '邮箱不能为空',
            'email.email' => '邮箱格式不正确',
            'email.unique' => '该邮箱已被注册',
            'password.required' => '密码不能为空',
            'password.min' => '密码至少8个字符',
            'password.confirmed' => '两次密码不一致',
            'phone.regex' => '手机号格式不正确',
            'avatar.image' => '头像必须是图片',
            'avatar.max' => '头像不能超过1MB',
        ];
    }

    public function attributes()
    {
        return [
            'name' => '姓名',
            'email' => '邮箱',
            'password' => '密码',
            'phone' => '手机号',
            'avatar' => '头像',
        ];
    }

    protected function prepareForValidation()
    {
        if ($this->phone) {
            $this->merge([
                'phone' => preg_replace('/[^0-9]/', '', $this->phone),
            ]);
        }
    }

    protected function passedValidation()
    {
        $this->merge([
            'password' => bcrypt($this->password),
        ]);
    }
}

10.2 更新用户请求 #

php
// app/Http/Requests/UpdateUserRequest.php
namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class UpdateUserRequest extends FormRequest
{
    public function authorize()
    {
        return $this->user()->can('update', $this->user);
    }

    public function rules()
    {
        $userId = $this->route('user')->id;
        
        return [
            'name' => 'required|string|max:255',
            'email' => 'required|email|unique:users,email,' . $userId,
            'phone' => 'nullable|regex:/^1[3-9]\d{9}$/',
            'password' => 'nullable|min:8|confirmed',
        ];
    }
}

10.3 控制器使用 #

php
class UserController extends Controller
{
    public function store(StoreUserRequest $request)
    {
        $user = User::create($request->validated());
        
        if ($request->hasFile('avatar')) {
            $user->avatar = $request->file('avatar')->store('avatars');
            $user->save();
        }
        
        return redirect()->route('users.show', $user)
            ->with('success', '用户创建成功');
    }

    public function update(UpdateUserRequest $request, User $user)
    {
        $data = $request->validated();
        
        if (empty($data['password'])) {
            unset($data['password']);
        }
        
        $user->update($data);
        
        return redirect()->route('users.show', $user)
            ->with('success', '用户更新成功');
    }
}

十一、最佳实践 #

11.1 请求类命名 #

text
StoreUserRequest    - 创建用户
UpdateUserRequest   - 更新用户
DeleteUserRequest   - 删除用户
ImportUserRequest   - 导入用户

11.2 共享基类 #

php
// app/Http/Requests/BaseRequest.php
class BaseRequest extends FormRequest
{
    protected function failedAuthorization()
    {
        abort(403, '无权执行此操作');
    }
}

// 使用
class StoreUserRequest extends BaseRequest
{
    // ...
}

11.3 复用规则 #

php
class UserRequest extends FormRequest
{
    protected function commonRules()
    {
        return [
            'name' => 'required|string|max:255',
            'email' => 'required|email',
        ];
    }
}

class StoreUserRequest extends UserRequest
{
    public function rules()
    {
        return array_merge($this->commonRules(), [
            'password' => 'required|min:8',
        ]);
    }
}

十二、总结 #

12.1 核心方法 #

方法 说明
authorize() 授权检查
rules() 验证规则
messages() 错误消息
attributes() 字段名称
prepareForValidation() 验证前处理
passedValidation() 验证后处理
withValidator() 验证钩子

12.2 下一步 #

掌握了表单请求后,让我们继续学习 认证系统,了解Laravel用户认证!

最后更新:2026-03-28