Laravel 5x 自定义数据验证
Laravel本身内置了许多好用的数据校验规则,拿来即用,但这远远不够,我们需要自定义自己的验证规则是必要的,先看一个简单的示例:
简单验证
直接在
app\Providers\AppServiceProvider.php
里扩展Validator
类
打开 app\Providers\AppServiceProvider.php
,在 boot()
方法里添加我们自己的验证规则,比方说我们需要一个验证是祖国的手机号码(+86):
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Validator;
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
Validator::extend('cn_phone', function($attribute, $value, $parameters) {
return substr($value, 0, 3) == '+86';
});
}
}
参考文档我们发现,自定义验证器闭包接收四个参数,分别是要验证的属性名称、属性值、传递给规则的参数数组以及 Validator
实例。
这里:cn_phone
是我们将在验证请求类中使用的规则名称,验证通过返回 TRUE
, 失败返回 FALSE
,参数 $attribute
是要验证的字段的名称,参数 $parameters
用于更复杂的验证规则,像 Laravel 中默认存在的 min:x
或 same:field
这种。
下面演示:
定义一个 /form_store
路由指向 FormController
的 postForm
方法,再定义个请求类 CreateUserRequest
依赖注入。
public function postForm(CreateUserRequest $request)
{
return "Success!";
}
app\Http\Requests\CreateUserRequest.php
<?php
namespace App\Http\Requests;
class CreateUserRequest extends Request {
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'phone' => 'required|cn_phone',
];
}
}
我就用自带的 welcome.blade.php
模板页面给大家演示一下:
<div class="content">
<div class=title m-b-md>
@if(count($errors) > 0)
@foreach($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
@endif
<form action="{{ url('form_store') }}" method="post">
{{ csrf_field() }}
<input type="text" name="phone">
<input type="submit" value="确认">
</form>
</div>
</div>
preview:
image好像出错提示出来了,有木有,但是这不是我们想要的,我们要自定义一个错误消息。打开 resources/lang/en/validation.php
找到
'custom' => [
'attribute-name' => [
'rule-name' => 'custom-message',
],
],
按照此格式要求,改写成我们定义的验证字段和对应的返回错误消息提示:
'custom' => [
'phone' => [
'zn_phone' => '请加手机号的国际区号+86',
],
],
再次验证:
image
这还远远不够,对于复杂的数据验证呢?
复杂验证
自定义的
Validator
类
假设有这么个验证要求,是phone
和
首先我们想到的是这个自定义验证类放哪里好呢?这里我个人建议在
app
下新建一个目录,我取名为Librarys
,这里放一些公共函数库,第三方支付模块以及我们的自定义验证类等等。上代码:
app\Providers\MyValidator.php
<?php
namespace App\Librarys;
use Illuminate\Validation\Validator;
class MyValidator extends Validator {
public function validateEmptyWith($attribute, $value, $parameters)
{
return ($value != '' && $this->getValue($parameters[0]) != '') ? false : true;
}
}
App\Providers\AppServiceProvider
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\ServiceProvider;
use App\Librarys;
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
Validator::resolver(function($translator, $data, $rules, $messages)
{
return new MyValidator($translator, $data, $rules, $messages);
});
}
resources\lang\en\validation.php
'custom' => [
'phone' => [
'empty_with' => '只能填一个字段,不能同时',
],
],
定义好验证类,这个类只是扩展了 Laravel
的内置验证基类,想让我们的验证规则被 Laravel
“承认”,必须进入 AppServiceProvider
用 boot
方法启动载入。
分析返回条件,想一想如果达不到上面的“需求”,那意味着:
- 不输入手机号,输入邮箱
- 不输入手机号,不输入邮箱
- 两者都不输入
满足这三个条件即为验证通过,那么取反后判断条件如上,大家不用纠结这个判断,着重看 $this->getValue($parameters[0])
这个方法,参数数组 $parameters[0]
为对应第一个验证规则,类似 min:xxx
, 这里是 empty_with:email
,通过该参数获取 email
对应的值传入 getValue()
中再返回 bool
值。问题来了,为什么是 empty_with:email
不是 emptyWith:email
或其他的呢,其实框架内部已经为我们处理好了名称的对应的格式,我们自定义的验证类里的验证方法必须以 validate
开头然后接小驼峰命名,对应验证规则的名称就是下划线的方式。这点要牢记。
效果图就不放了,大家可以尝试下,这样,基本上我们单独自定义的验证类结构就比较清晰了,利用面向对象的方式抽离出独立的验证类,更符合单一职责原则,这里其实还可以优化,比如独立出一个 ValidationExtensionServiceProvider extends ServiceProvider
:
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 );
} );
}
}
然后告诉 laravel 载入该服务,app/config/app.php
里添加进去。这样就更符合 Laravel Way
了。