关于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 相关文章推荐
PHP生成带有雪花背景的验证码
Oct 09 PHP
PHP 一个页面执行时间类代码
Mar 05 PHP
创建数据库php代码 用PHP写出自己的BLOG系统
Apr 12 PHP
Laravel 5框架学习之表单验证
Apr 08 PHP
浅谈PHP中Stream(流)
Jun 08 PHP
求帮忙修改个php curl模拟post请求内容后并下载文件的解决思路
Sep 20 PHP
Zend Framework教程之Zend_Helpers动作助手ViewRenderer用法详解
Jul 20 PHP
PHP实现添加购物车功能
Mar 06 PHP
使用WAMP搭建PHP本地开发环境
May 10 PHP
php对xml文件的增删改查操作实现方法分析
May 19 PHP
PHP实现多图上传和单图上传功能
May 17 PHP
php数组函数array_push()、array_pop()及array_shift()简单用法示例
Jan 26 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 PDO fetch 模式各种参数的输出结果一览
2015/01/07 PHP
PHP文件生成的图片无法使用CDN缓存的解决方法
2015/06/20 PHP
PHP会话控制实例分析
2016/12/24 PHP
JQuery插件Style定制化方法的分析与比较
2012/05/03 Javascript
js弹出层(jQuery插件形式附带reLoad功能)
2013/04/12 Javascript
浏览器窗口大小变化时使用resize事件对框架不起作用的解决方法
2014/05/11 Javascript
网站接入QQ登录的两种方法
2014/07/22 Javascript
JavaScript按值删除数组元素的方法
2015/04/24 Javascript
使用CDN和AJAX加速WordPress中jQuery的加载
2015/12/05 Javascript
BootStrap点击下拉菜单项后显示一个新的输入框实现代码
2016/05/16 Javascript
js判断一个字符串是以某个字符串开头的简单实例
2016/12/27 Javascript
深入浅析JSONAPI在PHP中的应用
2017/12/24 Javascript
JS中Map和ForEach的区别
2018/02/05 Javascript
可能被忽略的一些JavaScript数组方法细节
2019/02/28 Javascript
详解原生JS动态添加和删除类
2019/03/26 Javascript
ES6 Promise对象的应用实例分析
2019/06/27 Javascript
layuiAdmin循环遍历展示商品图片列表的方法
2019/09/16 Javascript
[02:59]DOTA2完美大师赛主赛事第三日精彩集锦
2017/11/25 DOTA
[01:16:12]完美世界DOTA2联赛PWL S2 FTD vs Inki 第一场 11.21
2020/11/23 DOTA
使用BeautifulSoup爬虫程序获取百度搜索结果的标题和url示例
2014/01/19 Python
python比较两个列表是否相等的方法
2015/07/28 Python
关于Django外键赋值问题详解
2017/08/13 Python
Python enumerate函数功能与用法示例
2019/03/01 Python
利用Pytorch实现简单的线性回归算法
2020/01/15 Python
Python动态导入模块:__import__、importlib、动态导入的使用场景实例分析
2020/03/30 Python
python中count函数知识点浅析
2020/12/17 Python
Pycharm创建python文件自动添加日期作者等信息(步骤详解)
2021/02/03 Python
canvas学习笔记之绘制简单路径
2019/01/28 HTML / CSS
中学生校园广播稿
2014/01/16 职场文书
白血病捐款倡议书
2014/05/14 职场文书
工程学毕业生自荐信
2014/06/14 职场文书
2014党员自我评议表范文
2014/09/20 职场文书
2015年重阳节慰问信
2015/03/23 职场文书
忆童年!用Python实现愤怒的小鸟游戏
2021/06/07 Python
python异步的ASGI与Fast Api实现
2021/07/16 Python
Zabbix对Kafka topic积压数据监控的解决方案
2022/07/07 Servers