关于Laravel参数验证的一些疑与惑


Posted in PHP onNovember 19, 2019

验证器怎么创建的,谁创建的

Laravel 文档调用验证器,除了通过控制器,还有就是通过Facades的方式创建验证器对象。Validator::make($data,$rule,$message)。

config/app.php 中注册了'Validator' => Illuminate\Support\Facades\Validator::class。

<?php

namespace Illuminate\Support\Facades;

/**
 * @see \Illuminate\Validation\Factory
 */
class Validator extends Facade
{
  /**
   * Get the registered name of the component.
   *
   * @return string
   */
  protected static function getFacadeAccessor()
  {
    return 'validator';
  }
}

从上面可以看出,Validator的实际实现类是容器中的validator对象,那这个validator对象是哪个?

<?php

namespace Illuminate\Foundation;
...
class Application extends Container implements ApplicationContract, HttpKernelInterface
{
  ...
  public function registerCoreContainerAliases()
  {
    foreach ([
      ...
      'validator'=> [
        \Illuminate\Validation\Factory::class,
        \Illuminate\Contracts\Validation\Factory::class
      ],
    ])
    ...
  }
  ...
}

可以看出,最终创建验证器是通过实现\Illuminate\Contracts\Validation\Factory接口的\Illuminate\Validation\Factory类创建的。再来看看,这个工厂类怎么创建实际的验证器的。

//\Illuminate\Contracts\Validation\Factory 源码

protected function resolve(array $data, array $rules, array $messages, array $customAttributes)
{
  if (is_null($this->resolver)) {
    return new Validator(
      $this->translator,
      $data,
      $rules,
      $messages,
      $customAttributes
    );
  }

  return call_user_func(
    $this->resolver,
    $this->translator,
    $data,
    $rules,
    $messages,
    $customAttributes
  );
}

到这里,可以看出Laravel的验证器的创建都是通过特定的工厂类创建。

如果需要自定义验证器类(比如我需要把5.8的一些新功能迁移到5.5的版本上),有两种方式:

一,创建一个自定义的工厂类。然后在AppServiceProvider中重新绑定新的验证器工厂创建类;

二,AppServiceProvider中通过resolver方法设置工厂类的resolver属性,接管验证器的实例化,例如:

Validator::resolver(function($translator, $data, $rules, $messages, $customAttributes){
  return new ExtendValidator($translator, $data, $rules, $messages, $customAttributes);
});

如何自定义验证规则

Laravel本身提供了很多通用的参数验证规则,但是对于一些特定的场景,还是需要提供验证规则的扩展。

Laravel验证规则的扩展有两种方式。

1 通过extend方法扩展

//这是一个简单的参数比较的验证规则,Laravel5.8中提供,Laravel5.5中未提供
//验证规则如下: 'max_num'=>'gte:min',
Validator::extend('gte',function($attribute, $value, $parameters, $validator){
  if($value>=data_get($validator->getData(),$parameters[0]))
  {
    return true;
  }
  return false;
});
//\Illuminate\Contracts\Validation\Factory 源码
public function extend($rule, $extension, $message = null)
{
  $this->extensions[$rule] = $extension;

  if ($message) {
    $this->fallbackMessages[Str::snake($rule)] = $message;
  }
}
//\Illuminate\Validation\Validator 源码
protected function callExtension($rule, $parameters)
{
  $callback = $this->extensions[$rule];

  if (is_callable($callback)) {
    return call_user_func_array($callback, $parameters);
  } elseif (is_string($callback)) {
    return $this->callClassBasedExtension($callback, $parameters);
  }
}

protected function validateAttribute($attribute, $rule)
{
  ...
  $method = "validate{$rule}";
  if ($validatable && ! $this->$method($attribute, $value, $parameters, $this)) {
    $this->addFailure($attribute, $rule, $parameters);
  }
}

public function __call($method, $parameters)
{
  $rule = Str::snake(substr($method, 8));

  if (isset($this->extensions[$rule])) {
    return $this->callExtension($rule, $parameters);
  }

  throw new BadMethodCallException(sprintf(
    'Method %s::%s does not exist.', static::class, $method
  ));
}

Factory提供了extend方法用于扩展规则验证方法。所有的扩展规则最终都会被传到验证器中。验证器在验证参数的过程中,如果找到匹配的验证规则,则直接进行验证。否则调用魔术方法__call查找扩展验证函数。扩展函数返回布尔值,返回true则表示验证通过,返回false表示验证失败。

2 通过自定义规则类扩展

Laravel 中提供了Illuminate\Contracts\Validation\Rule接口,只有实现了这个接口的类都认为是符合的自定义验证规则类。

<?php

namespace Illuminate\Contracts\Validation;

interface Rule
{
  /**
   * Determine if the validation rule passes.
   *
   * @param string $attribute
   * @param mixed $value
   * @return bool
   */
  public function passes($attribute, $value);

  /**
   * Get the validation error message.
   *
   * @return string
   */
  public function message();
}

自定义规则类需要实现的方法有passes方法,用于验证参数是否合法。message方法,用于提供验证失败的错误提示信息。

使用自定义验证类,相对于extend方法扩展有一个很大的bug就是无法在自定义类中获取到当期的验证器对象。从而导致在当前扩展的验证规则中,只能过获取到需要验证的数据,而获取不到其他的字段数据,无法进行联合字段的验证。像上面比较两个字段的大小的验证规则就无法实现。

如果想要通过自定义验证规则类实现上面两个字段大小比较的验证规则,则需要自定义验证类,修改validateUsingCustomRule方法,将当期验证器传入到自定义验证规则实例对象中去。

protected function validateUsingCustomRule($attribute, $value, $rule)
{
  if(method_exists($rule, 'setValidator'))
  {
    $rule->setValidator($this);
  }
  return parent::validateUsingCustomRule($attribute,$value,$rule);
}

如何实现用当期类方法作为验证规则验证函数

像Yii2中,因为基本上所有的对象都有验证方法,所以很容易用当期类方法作为验证规则验证函数。

例如,一个验证规则如下,表示用当期类的validateMinNum对参数进行验证,那么,这样的一个功能,如何在Laravel中实现呢。

['min_num'=>'validateMinNum']

方法1 通过自定义类实现 Laravel提供了ClosureValidationRule自定义验证类,用来添加回调函数的验证。

例如

$rule = [
  'min'=>new ClosureValidationRule([$this,'checkv'])
];
$data = ['min'=>10];
$v = Validator::make($data,$rule);

方法2 通过extend方式实现

$rule = [
  'min'=>'checkv'
];
Validator::extend('checkv',[$this,'checkv']);

但是这种方式对验证器的影响是全局的。不建议使用。

总结

通过以上源码的学习,可以看出Laravel验证器的创建都是用过验证器工厂类创建的。如果需要自定义验证器,可以通过修改验证器工厂类,或者设置验证器工厂类的resolver属性接管验证器的实例化。

验证规则的扩展有两种方式,一种是通过extend方式实现。extend方式对验证器的影响是全局的,整个运行进程有效。可以获取到验证器本身,因此可以做多个字段关系的验证;另一种是通过自定义规则类实现。自定义规则了只对使用自定义规则类的验证有效。但是自定义规则类本身无法直接获取到验证器本身,不能够做多个字段关系的验证。如果需要实现,则需要使用自定义验证器,将验证器传入到验证规则中去。

Laravel本身提供了ClosureValidationRule的验证规则用于处理回调函数验证规则。同时也可以使用extend方式进行回调函数的验证。

好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对三水点靠木的支持。

PHP 相关文章推荐
探讨:array2xml和xml2array以及xml与array的互相转化
Jun 24 PHP
php使用百度翻译api示例分享
Jan 31 PHP
PHP正则匹配日期和时间(时间戳转换)的实例代码
Dec 14 PHP
php微信公众号开发(2)百度BAE搭建和数据库使用
Dec 15 PHP
thinkphp3.2中实现phpexcel导出带生成图片示例
Feb 14 PHP
PHP ADODB实现事务处理功能示例
May 25 PHP
thinkPHP5框架中widget的功能与用法详解
Jun 11 PHP
php微信开发之谷歌测距
Jun 14 PHP
php代码调试利器firephp安装与使用方法分析
Aug 21 PHP
PHP抽象类基本用法示例
Dec 28 PHP
phpstorm 配置xdebug的示例代码
Mar 31 PHP
php中yar框架实例用法讲解
Dec 27 PHP
php传值和传引用的区别点总结
Nov 19 #PHP
php 使用 __call实现重载功能示例
Nov 18 #PHP
PHP中通过getopt解析GNU C风格命令行选项
Nov 18 #PHP
php 多继承的几种常见实现方法示例
Nov 18 #PHP
Yii框架 session 数据库存储操作方法示例
Nov 18 #PHP
PHP cookie与session会话基本用法实例分析
Nov 18 #PHP
php pdo连接数据库操作示例
Nov 18 #PHP
You might like
PHP二维数组实现去除重复项的方法【保留各个键值】
2017/12/21 PHP
PHP设计模式之工厂模式(Factory Pattern)的讲解
2019/03/21 PHP
jQuery 自动增长的文本输入框实现代码
2010/04/02 Javascript
JavaScript 原型与继承说明
2010/06/09 Javascript
JavaScript与Image加载事件(onload)、加载状态(complete)
2011/02/14 Javascript
JS清除IE浏览器缓存的方法
2013/07/26 Javascript
javascript读写XML实现广告轮换(兼容IE、FF)
2013/08/09 Javascript
js Dialog 去掉右上角的X关闭功能
2014/04/23 Javascript
JavaScript字符串对象replace方法实例(用于字符串替换或正则替换)
2014/10/16 Javascript
javascript中mouseover、mouseout使用详解
2015/07/19 Javascript
jQuery实现获取隐藏div高度的方法示例
2017/02/09 Javascript
jQuery实现定时隐藏对话框的方法分析
2018/02/12 jQuery
vue 设置路由的登录权限的方法
2018/07/03 Javascript
使用javascript做时间倒数读秒功能的实例
2019/01/23 Javascript
在vue中使用inheritAttrs实现组件的扩展性介绍
2020/12/07 Vue.js
python pdb调试方法分享
2014/01/21 Python
python中kmeans聚类实现代码
2018/02/23 Python
对python使用http、https代理的实例讲解
2018/05/07 Python
基于python指定包的安装路径方法
2018/10/27 Python
Python3实现取图片中特定的像素替换指定的颜色示例
2019/01/24 Python
使用NumPy读取MNIST数据的实现代码示例
2019/11/20 Python
使用python去除图片白色像素的实例
2019/12/12 Python
Python基于pip实现离线打包过程详解
2020/05/15 Python
Django使用rest_framework写出API
2020/05/21 Python
keras多显卡训练方式
2020/06/10 Python
python使用nibabel和sitk读取保存nii.gz文件实例
2020/07/01 Python
Python如何实现线程间通信
2020/07/30 Python
python利用xlsxwriter模块 操作 Excel
2020/10/14 Python
英国手工制作的现代与经典的沙发和床:Love Your Home
2020/09/26 全球购物
行政助理求职自荐信
2013/10/26 职场文书
中专药剂专业应届毕的自我评价
2013/12/27 职场文书
初中毕业生的自我评价
2014/03/03 职场文书
创建青年文明号材料
2014/05/09 职场文书
合理缓解职场压力,让你随时保持最佳状态!
2019/06/21 职场文书
Python使用random模块实现掷骰子游戏的示例代码
2021/04/29 Python
canvas绘制折线路径动画实现
2021/05/12 Javascript