在使用Laravel 5中的表单请求验证时,如何加入自定义验证规则

52

我正在使用laravel 5中的表单请求验证方法来验证请求。我想通过表单请求验证方法添加自己的验证规则。我的请求类如下所示。我想添加一个名为numeric_array的自定义验证规则到items字段。

  protected $rules = [
      'shipping_country' => ['max:60'],
      'items' => ['array|numericarray']
];

下面是我的自定义函数

 Validator::extend('numericarray', function($attribute, $value, $parameters) {
            foreach ($value as $v) {
                if (!is_int($v)) {
                    return false;
                }
            }
            return true;
        });

如何在laravel5中使用此验证方法与关于表单请求验证?

9个回答

53

尽管上面的答案是正确的,在许多情况下,您可能希望仅为特定表单请求创建自定义验证。您可以利用 Laravel FormRequest 并使用依赖注入来扩展验证工厂。我认为这个解决方案比创建服务提供程序要简单得多。

以下是如何实现的。

use Illuminate\Validation\Factory as ValidationFactory;

class UpdateMyUserRequest extends FormRequest {

    public function __construct(ValidationFactory $validationFactory)
    {

        $validationFactory->extend(
            'foo',
            function ($attribute, $value, $parameters) {
                return 'foo' === $value;
            },
            'Sorry, it failed foo validation!'
        );

    }

    public function rules()
    {
        return [
            'username' => 'foo',
        ];
    }
}

50

像您所做的那样使用Validator :: extend()实际上是完全可以的,您只需要将其放在服务提供者中,就像这样:

<?php namespace App\Providers;

use Illuminate\Support\ServiceProvider;

class ValidatorServiceProvider extends ServiceProvider {

    public function boot()
    {
        $this->app['validator']->extend('numericarray', function ($attribute, $value, $parameters)
        {
            foreach ($value as $v) {
                if (!is_int($v)) {
                    return false;
                }
            }
            return true;
        });
    }

    public function register()
    {
        //
    }
}

然后通过将提供者添加到config/app.php列表中来注册提供者:

'providers' => [
    // Other Service Providers

    'App\Providers\ValidatorServiceProvider',
],

现在您可以在任何地方使用numericarray验证规则了。


我该如何让自定义错误消息在这里起作用?仅仅在模型中添加一个$messages数组似乎并不能解决问题。 - Dubby
1
@Dubby 在你的 Request 类中,你可以添加一个新的方法 messages() 并返回自定义消息的数组。例如:return [ 'field_name.custom_validator_name' => '您的错误消息']; - Milan Maharjan

21

接受的答案适用于全局验证规则,但很多时候您将验证特定于表单的非常特定的条件。在这种情况下,我建议采用以下方法(似乎是 Laravel 源代码中的某些意图,详见FormRequest.php 的第 75 行):

在父请求中添加一个验证器方法,您的请求将继承:

<?php namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;
use Validator;

abstract class Request extends FormRequest {

    public function validator(){

        $v = Validator::make($this->input(), $this->rules(), $this->messages(), $this->attributes());

        if(method_exists($this, 'moreValidation')){
            $this->moreValidation($v);
        }

        return $v;
    }
}

现在,您所有的具体请求将看起来像这样:

<?php namespace App\Http\Requests;

use App\Http\Requests\Request;

class ShipRequest extends Request {

    public function rules()
    {
        return [
            'shipping_country' => 'max:60',
            'items' => 'array'
        ];
    }

    // Here we can do more with the validation instance...
    public function moreValidation($validator){

        // Use an "after validation hook" (see laravel docs)
        $validator->after(function($validator)
        {
            // Check to see if valid numeric array
            foreach ($this->input('items') as $item) {
                if (!is_int($item)) {
                    $validator->errors()->add('items', 'Items should all be numeric');
                    break;
                }
            }
        });
    }

    // Bonus: I also like to take care of any custom messages here
    public function messages(){
        return [
            'shipping_country.max' => 'Whoa! Easy there on shipping char. count!'
        ];
    }
}

5
自 5.3.23 版本之后,实际上已经内置了名为 withValidator 的功能。请参考链接:https://github.com/laravel/framework/commit/bf8a36ac3df03a2c889cbc9aa535e5cf9ff48777 - brad

11

自定义验证规则对象

一种方法是使用自定义验证规则对象,通过这种方式,您可以定义任意数量的规则而不需要在提供程序和控制器/服务中进行更改以设置新规则。

php artisan make:rule NumericArray

在NumericArray.php文件中

namespace App\Rules;
class NumericArray implements Rule
{
   public function passes($attribute, $value)
   {
     foreach ($value as $v) {
       if (!is_int($v)) {
         return false;
       }
     }
     return true;
   }


  public function message()
  {
     return 'error message...';
  }
}

然后在表单请求中有

use App\Rules\NumericArray;
.
.
protected $rules = [
      'shipping_country' => ['max:60'],
      'items' => ['array', new NumericArray]
];

3
当我在使用JS验证 (https://github.com/proengsoft/laravel-jsvalidation) 时,出现了错误: trim() expects parameter 1 to be string, object given。我通过在NumericArray类中添加"public function __toString(){return 'NumericArray ';}"解决了这个问题。https://github.com/mpociot/laravel-apidoc-generator/issues/247 - sudip
1
当您调用new NumericArray时,是否有一种方法可以传递额外的参数?例如,我有一个自定义规则对象,现在它只检查一个模型(Address),但我想将模型作为参数传递: public function passes($attribute, $value) { return !is_null(\App\Address::whereUuid($value)->where('user_id', request()->user()->id)->first()); } - Kyle Ridolfo
@kyleRidolfo 也许你可以在初始化时定义构造函数,像这样传递你的模型:item => [new NumericArray($model)],然后在规则类 NumericArray 中读取它并初始化一个类属性,你可以在 passes 方法中使用它:private $classProperty; public function __construct($model) { $this->classProperty = $model; } - gk.
@GaneshKarki 啊,是的,那很有道理,不知道我当时在想什么!:) 谢谢! - Kyle Ridolfo

11

除了Adrian Gunawan的解决方案之外,现在也可以这样处理:

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class StoreBlogPost extends FormRequest
{
    public function rules()
    {
        return [
            'title' => ['required', 'not_lorem_ipsum'],
        ];
    }

    public function withValidator($validator)
    {
        $validator->addExtension('not_lorem_ipsum', function ($attribute, $value, $parameters, $validator) {
            return $value != 'lorem ipsum';
        });

        $validator->addReplacer('not_lorem_ipsum', function ($message, $attribute, $rule, $parameters, $validator) {
            return __("The :attribute can't be lorem ipsum.", compact('attribute'));
        });
    }
}


无论如何,这个方法没有起作用。 - dılo sürücü
顺便提一句,我们还可以使用 $validator->getData()['another_request_parameter'] 访问“addExtension”函数中的其他请求参数值:$validator->addExtension('not_equal', function ($attribute, $value, $parameters, $validator) { return $value != $validator->getData()['another_request_parameter']; }); - otidh

6

您需要在您的Request类中覆盖getValidatorInstance方法,例如这样:

protected function getValidatorInstance()
{
    $validator = parent::getValidatorInstance();
    $validator->addImplicitExtension('numericarray', function($attribute, $value, $parameters) {
        foreach ($value as $v) {
            if (!is_int($v)) {
                return false;
            }
        }
        return true;
    });

    return $validator;
}

你真的需要以这种方式做吗?据我所知,在服务提供者中使用Validator::extend()应该足以使规则在全局范围内可用。 - lukasgeiter
@lukasgeiter 我创建了但是遇到错误信息:方法[validatePasscheck]不存在。我该如何解决? - gsk
@ShihabudheenMuhammed,你注册了服务提供者吗?你需要在config/app.phpproviders下添加它。 - lukasgeiter
@lukasgeiter 谢谢你的回答。我添加了这些行,但现在我又遇到了另一个错误:“类 translator 不存在”。:( - gsk
3
这个回答比创建整个服务提供商要好得多,特别是当你只需要使用这个规则一次时。 - kjdion84
显示剩余7条评论

4

您无需扩展验证器来验证数组项,您可以使用"*"验证数组的每个项目,如您在数组验证中所见。

protected $rules = [
      'shipping_country' => ['max:60'],
      'items' => ['array'],
      'items.*' => 'integer'
];

尽管这个回答没有回答原帖的标题问题,但它是原帖实际情况的更好解决方案。 - Spencer O'Reilly

1

这个页面上的所有答案都可以解决您的问题,但是...根据Laravel规则,唯一正确的方法是来自Ganesh Karki的解决方案。

举个例子:

让我们以填写夏季奥林匹克运动会事件-年份和城市的表单为例。首先创建一个表单。

<form action="/olimpicyear" method="post">
  Year:<br>
  <input type="text" name="year" value=""><br>
  City:<br>
  <input type="text" name="city" value=""><br><br>
  <input type="submit" value="Submit">
</form> 

现在,让我们创建一个验证规则,你只能输入奥林匹克运动会的年份。这些是条件:
  1. 运动会始于1896年
  2. 年份不能大于当前年份
  3. 数字应该被4整除
让我们运行以下命令: php artisan make:rule OlympicYear Laravel将生成一个文件 app/Rules/OlympicYear.php。将该文件更改为以下内容:
namespace App\Rules; use Illuminate\Contracts\Validation\Rule;
class OlympicYear implements Rule {
/**
 * Determine if the validation rule passes.
 *
 * @param  string  $attribute
 * @param  mixed  $value
 * @return bool
 */
public function passes($attribute, $value)
{
    // Set condition that should be filled
    return $value >= 1896 && $value <= date('Y') && $value % 4 == 0;
}

/**
 * Get the validation error message.
 *
 * @return string
 */
public function message()
{
    // Set custom error message.
    return ':attribute should be a year of Olympic Games';
}

}

最后,我们如何使用这个类?在控制器的store()方法中,我们有以下代码:
public function store(Request $request)
{
    $this->validate($request, ['year' => new OlympicYear]);
}

如果你想按照Laravel的惯例创建验证,可以通过下面的链接来查看教程。它很容易理解,解释得非常清楚。这对我帮助很大。原始教程链接在Tutorial link

如何在Laravel中创建自定义验证规则 - Gediminas

0

对我而言,lukasgeiter 给出的解决方案有效,但有所不同的是我们要创建一个带有自定义验证的类。例如,在 Laravel 5.2.* 中,下面的示例用于向日期范围添加验证,其中第二个日期必须等于或大于第一个日期:

在 app/Providers 中创建 ValidatorExtended.php

<?php
namespace App\Providers;
use Illuminate\Validation\Validator as IlluminateValidator;

class ValidatorExtended extends IlluminateValidator {

private $_custom_messages = array(
 "after_or_equal" => ":attribute debe ser una fecha posterior o igual a 
 :date.",
);

public function __construct( $translator, $data, $rules, $messages = array(),      
$customAttributes = array() ) {
  parent::__construct( $translator, $data, $rules, $messages, 
  $customAttributes );
  $this->_set_custom_stuff();
}

protected function _set_custom_stuff() {
   //setup our custom error messages
  $this->setCustomMessages( $this->_custom_messages );
}

/**
 * La fecha final debe ser mayor o igual a la fecha inicial
 *
 * after_or_equal
 */
protected function validateAfterOrEqual( $attribute, $value, $parameters, 
$validator) {
   return strtotime($validator->getData()[$parameters[0]]) <= 
  strtotime($value);
}

}   //end of class

好的,现在让我们创建服务提供者。在app/Providers目录下创建ValidationExtensionServiceProvider.php文件,然后我们编写代码

<?php
namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Validator;

class ValidationExtensionServiceProvider extends ServiceProvider {

public function register() {}

public function boot() {
  $this->app->validator->resolver( function( $translator, $data, $rules, 
  $messages = array(), $customAttributes = array() ) {
    return new ValidatorExtended( $translator, $data, $rules, $messages, 
    $customAttributes );
} );
}

}   //end of class

现在我们需要告诉 Laravel 加载这个服务提供者,在 config/app.php 的 providers 数组末尾添加即可。
//Servicio para extender validaciones
App\Providers\ValidationExtensionServiceProvider::class,

现在我们可以在函数规则中使用此验证来处理请求。
public function rules()
{
  return [
    'fDesde'     => 'date',
    'fHasta'     => 'date|after_or_equal:fDesde'
 ];
}

或者在Validator::make中:

$validator = Validator::make($request->all(), [
    'fDesde'     => 'date',
    'fHasta'     => 'date|after_or_equal:fDesde'
], $messages);

你必须注意到,进行验证的方法名称具有前缀 validate,并且采用驼峰式样式 validateAfterOrEqual,但是当您使用验证规则时,每个大写字母都会被替换为下划线和小写字母。

我从https://www.sitepoint.com/data-validation-laravel-right-way-custom-validators//中获取了所有这些详细信息。感谢他们。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接