登录
原创

laravel 表单验证及自定义验证规则

发布于 2020-10-15 阅读 1160
  • 后端
  • PHP
  • Laravel
原创

控制器参数校验

弊端:

  1. 与控制器耦合度太高
  2. 代码复用性不高,维护成本大

创建控制器添加校验规则

     /**
     * 存储一篇新的博客文章
     *
     * @param  Request  $request
     * @return Response
     */
    public function store(Request $request)
    {
        $validatedData = $request->validate([
            'title' => 'required|unique:posts|max:255',
            'body' => 'required',
        ]);
        
        //也可以是这样的数组规则
        $validatedData = $request->validate([
            'title' => ['required', 'unique:posts', 'max:255'],
            'body' => ['required'],
        ]);

    }

1、在首次验证失败时停止运行

有时候我们希望某个字段在第一次验证失败后就停止运行验证规则,只需要将 bail添加到规则中:

//在这个例子中,如果 title 字段没有通过 unique 规则,那么不会继续验证 max规则。
//规则会按照分配时的顺序来验证。
$request->validate([
    'title' => 'bail|required|unique:posts|max:255',
    'body' => 'required',
]);

2、嵌套字段的说明
如果你的 HTTP 请求包含「嵌套」参数(比如数组),你可以在验证规则中使用「点」语法来指定这些参数:

$request->validate([
    'title' => 'required|unique:posts|max:255',
    'author.name' => 'required',
    'author.description' => 'required',
]);

3、如果你的字段名称包含点,则可以通过使用反斜杠将点转义,以防止将其解释为 “点” 语法:

$request->validate([
    'title' => 'required|unique:posts|max:255',
    'v1\.0' => 'required',
]);

显示验证错误信息

如果传入的请求参数未通过给定的验证规则呢?正如前面所提到的,Laravel会自动将用户重定向到之前的位置。另外,所有的验证错误信息会自动存储到 闪存 session 中。
Laravel 会检查 session 数据中的错误,如果可用的话,将会自动将其绑定到视图中去。其中的$errors 变量是 Illuminate\Support\MessageBag 的一个实例。

技巧:
errors 由 web 中间件组提供的 Illuminate\View\Middleware\ShareErrorsFromSession中间件绑定到视图中。 当该中间件被应用后,errors 变量在您的视图中总是可用的,因此您可以假设 $errors变量总是被定义了且总是安全可用的。

所以,在如下的例子中,当表单验证失败时,用户将被重定向到控制器的 create 方法中,我们可在视图中显示错误信息:

<!-- /resources/views/post/create.blade.php -->

<h1>Create Post</h1>

@if ($errors->any())
    <div class="alert alert-danger">
        <ul>
            @foreach ($errors->all() as $error)
                <li>{{ $error }}</li>
            @endforeach
        </ul>
    </div>
@endif

<!--您亦可使用 @error Blade 指令方便地检查给定的属性是否存在验证错误信息。
在 @error 指令中,您可以输出 $message 变量以显示错误信息-->
<input id="title" type="text" class="@error('title') is-invalid @enderror">

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

表单验证

优点:

  1. 「表单请求」应对更复杂的验证逻辑,与控制器实现解耦
  2. 表单请求中验证规则可以独立处理实现规则复用

创建表单请求验证

面对更复杂的情况,您可以创建一个「表单请求」来应对更复杂的验证逻辑。表单请求是一个包含了验证逻辑的自定义请求类。

1、要创建一个表单请求类,请使用 make:request Artisan CLI 命令:

//创建验证码发送表单验证请求
php artisan make:request SendCode

该命令生成的类将被置于 app/Http/Requests目录中。如果这个目录不存在,在您运行make:request命令后将会创建这个目录。

2、让我们添加一些验证规则到 rules 方法中:

注:我们可以重写FormRequest中的大部分方法,来自定义校验和验证结果以及一些其他特殊需求


use App\Rules\CodeType;
use App\Rules\Mobile;
use Illuminate\Foundation\Http\FormRequest;

class SendCode extends FormRequest
{
    protected $mobile = '';

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

    /**
     * 参数校验规则
     * 可以向 rules 方法传入所需的任何依赖项(如:Request $requset)。
     * 它们将被 Laravel 服务容器 自动解析。
     *
     * @return array
     */
    public function rules()
    {
       
        return [
            'mobile' => ['exclude_if:codeType,3', 'required', new Mobile($this->mobile)],
            'codeType' => ['required', new CodeType()],
        ];
    }

    /**
     * 固定校验类型错误信息
     *
     * @return array
     */
    public function messages()
    {
        return [
            'mobile.required' => '手机号不能为空',
            'codeType.required' => '验证码类型不能为空',
        ];
    }
}

表单验证请求使用

在调用控制器方法之前验证传入的表单请求,这意味着你不需要在控制器中写任何验证逻辑:

    ...
   /* 
     *
     * @param  SendCode  $request
     * @return Response
     */
    public function store(SendCode $request)
    {
       $validated = $request->validated();

    }

如果验证失败,就会生成一个让用户返回到先前的位置的重定向响应。这些错误也会被闪存到 session 中,以便这些错误都可以在页面中显示出来。如果传入的请求是 AJAX,会向用户返回具有 422 状态代码和验证错误信息的 JSON 数据的 HTTP 响应。

表单验证请求钩子

如果你想在表单请求「之后」添加钩子,可以使用 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!');
        }
    });
}

注:从表单验证源码可以看出withValidator方法调用逻辑:


    /**
     * Get the validator instance for the request.
     *
     * @return \Illuminate\Contracts\Validation\Validator
     */
    protected function getValidatorInstance()
    {
        if ($this->validator) {
            return $this->validator;
        }

        $factory = $this->container->make(ValidationFactory::class);

        if (method_exists($this, 'validator')) {
            $validator = $this->container->call([$this, 'validator'], compact('factory'));
        } else {
            $validator = $this->createDefaultValidator($factory);
        }

        //这里有对这个钩子进行检查,如果表单请求中定义了钩子,那么可以在返回验证结果之前进行处理一些逻辑
        if (method_exists($this, 'withValidator')) {
            $this->withValidator($validator);
        }

        $this->setValidator($validator);

        return $this->validator;
    }

表单请求授权验证

表单请求类内也包含了 authorize方法。

如果表单请求需要校验身份的话可以在该方法中校验,否则请返回true或移除该方法

/**
 * 判断用户是否有请求权限
 *
 * @return bool
 */
public function authorize()
{
    //这个方法允许你在被调用的路由上获取其定义的 URI 参数,譬如下面例子中的 {comment} 参数   
    //Route::post('comment/{comment}');
    $comment = Comment::find($this->route('comment'));

    //表单请求都是继承了 Laravel 中的请求基类,所以我们可以使用 user 方法去获取当前认证登录的用户
    return $comment && $this->user()->can('update', $comment);
}

自定义错误消息

你可以通过重写表单请求的 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',
    ];
}

自定义验证规则

尽管 Laravel 提供了多种多样有用的校验规则;但您亦可进行自定义。注册自定义校验规则的方法之一便是使用规则对象。您可以使用 make:rule生成新的规则对象。

创建验证规则

们使用该命令生成一个校验手机号格式是否正确的规则, Laravel 会将新规则置于 app/Rules 目录中:

php artisan make:rule Mobile

当规则创建成功后,我们便可定义其行为。

规则对象包含两个方法: passes 和 message ,当然如果需要在规则校验的时候后传入特殊参数的话可以添加构造函数,用于初始化参数

passes 方法接收属性值及其名称,它应该返回以true 和 false表示的属性值是否通过验证的结果。 message方法应该返回验证失败时使用的错误信息:

<?php

namespace App\Rules;

use ComTools\Verify;
use Illuminate\Contracts\Validation\Rule;

/**
 * 手机号校验规则
 *
 * Class Password
 * @package App\Rules
 */
class Mobile implements Rule
{
    /**
     * 错误信息
     *
     * @var
     */
    protected $message = '手机号格式错误';

    protected $value;

    /**
     * Create a new rule instance.
     *
     *
     * Mobile constructor.
     * @param mixed $mobile
     */
    public function __construct($mobile = '')
    {
        $this->value = $mobile;
    }

    /**
     * 参数校验
     *
     * @param string $attribute
     * @param mixed $value
     * @return bool
     */
    public function passes($attribute, $value)
    {
        if ($this->value) {
            $value = $this->value;
        }

        if (!is_string($value) && !is_numeric($value)) {
            return false;
        }
        //简单校验手机号
        return Verify::isMobilePhone($value, true);
    }

    /**
     * 错误信息返回
     *
     * @return string
     */
    public function message()
    {
        return $this->message;
        //如果您想要从您的翻译文件中获取错误信息,您可以在您的 message 中使用 trans 助手方法:
        return trans('validation.mobile');
    }
}

自定义规则使用

一旦定义了规则,您便可以通过将规则的实例化与其他校验规则一起传递给验证器:
前文创建表单请求的时候有使用手机号校验规则Mobile

 /**
     * 参数校验规则
     * 可以向 rules 方法传入所需的任何依赖项(如:Request $requset)。
     * 它们将被 Laravel 服务容器 自动解析。
     *
     * @return array
     */
    public function rules()
    {
       
        return [
            'mobile' => ['exclude_if:codeType,3', 'required', new Mobile($this->mobile)],
            'codeType' => ['required', new CodeType()],
        ];
    }

使用闭包

如果您的规则在应用中仅仅使用一次,那您便可使用闭包来代替规则对象。闭包函数接收属性的方法,属性的值以及在校验失败时的回调函数 $fail:

$validator = Validator::make($request->all(), [
    'mobile' => [
        'required',
        function ($attribute, $value, $fail) {
            if (!Verify::isMobilePhone($value, true)) {
                $fail($attribute.' is invalid.');
            }
        },
    ],
]);
```>

评论区

励志做一条安静的咸鱼,从此走上人生巅峰。

1

0

0

举报