Laravel 表单验证:表单验证类 1 个改进

问题

我有一个非常复杂的表单验证场景,写在控制器方法里显得很庞杂臃肿,在 Laravel 中,我可以把验证逻辑单独写在某处么?

回答

对于更复杂的验证场景,Laravel 支持创建一个「表单请求」来处理更为复杂的逻辑。

创建表单请求

表单请求是包含验证逻辑的自定义请求类,我们可以使用 Artisan 命令 make:request 来创建表单请求类:

$ php artisan make:request StoreBlogPost

这会在 app/Http/Requests 目录下创建一个 app\Http\Requests\StoreBlogPost.php 文件,初始内容如下:

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class StoreBlogPost extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return false;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            //
        ];
    }
}

可以看到我们的 StoreBlogPost 类继承自 Illuminate\Foundation\Http\FormRequest 类(该类又继承自请求基类 Illuminate\Http\Request),初始内容中包含两个方法:authorizerules

表单请求验证规则

我们创建的 StoreBlogPost 类中的 rules 方法用于返回验证规则数组,比如我们可以添加以下一些验证规则到 rules 方法中:

public function rules()
{
    return [
        'title' => 'required|unique:posts|max:255',
        'body' => 'required',
    ];
}

那么验证规则又是如何运行的呢?只要你在控制器方法的参数中类型提示传入的请求,就会在调用控制器方法之前自动验证传入的表单请求,因此就不需要你再在控制器方法中写任何验证逻辑了,下面是某控制器的 store 方法示例:

/**
 * 存储传入的博客文章。
 *
 * @param  StoreBlogPost  $request
 * @return Response
 */
public function store(StoreBlogPost $request)
{
    // 传入的请求通过验证...

    // 获取通过验证的数据...
    $validated = $request->validated();
}

如果验证失败,就会生成一个将用户重定向回先前位置的响应。这些错误也会被闪存到会话中,使得它们可以显示在视图页面中。如果传入的是 AJAX 请求,返回的是具有 422 状态码和验证错误信息的 JSON 类型的 HTTP 响应。

表单请求授权验证

在表单请求类的 authorize 方法中,你可以检查经过身份验证的用户是否具有执行操作所需的权限。比如,你可以判断用户是否拥有更新文章评论的权限:

/**
 * 判断用户是否有权限做出此请求。
 *
 * @return bool
 */
public function authorize()
{
    $comment = Comment::find($this->route('comment'));

    return $comment && $this->user()->can('update', $comment);
}

由于所有的表单请求都是继承了 Laravel 中的请求基类 Illuminate\Http\Request,所以我们可以使用 user 方法去获取当前认证登录的用户。同时请注意上述例子中对 route 方法的调用。这个方法允许你在被调用的路由上获取其定义的 URI 参数,譬如下面例子中的 {comment} 参数:

Route::post('comment/{comment}');

如果 authorize 方法返回 false,则会自动返回一个包含 403 状态码的 HTTP 响应,也不会运行控制器的方法。

如果你打算在应用程序的其它部分处理授权逻辑,只需从 authorize 方法返回 true

/**
 * 判断用户是否有权限进行此请求。
 *
 * @return bool
 */
public function authorize()
{
    return true;
}

自定义错误消息

可以重写表单请求的 messages 方法来自定义错误消息。此方法应返回属性 / 规则对及其对应错误消息的数组:

/**
 * 获取已定义验证规则的错误消息。
 *
 * @return array
 */
public function messages()
{
    return [
        'title.required' => 'A title is required',
        'body.required'  => 'A message is required',
    ];
}

自定义验证属性

如果希望将验证消息的 :attribute 部分替换为自定义属性名称,则可以重写 attributes 方法来指定自定义名称。此方法应返回属性 / 名称对的数组:

/**
 * 获取验证错误的自定义属性。
 *
 * @return array
 */
public function attributes()
{
    return [
        'email' => 'email address',
    ];
}

添加表单请求后钩子

我们还可以在表单请求中用 withValidator 方法添加验证后钩子,这样可以在本身验证通过后再进行下一步的其它验证。这个方法接收一个完整的验证构造器,允许你在验证结果返回之前调用任何方法:

/**
 *  配置验证器实例。
 *
 * @param  \Illuminate\Validation\Validator  $validator
 * @return void
 */
public function withValidator($validator)
{
    $validator->after(function ($validator) {
        if ($this->somethingElseIsInvalid()) {
            $validator->errors()->add('field', 'Something is wrong with this field!');
        }
    });
}

参考

本文为 Wiki 文章,邀您参与纠错、纰漏和优化
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!