Laravel 验证类 实现 路由场景验证 和 控制器场景验证
基于不破坏框架原本用法、方便、简单的原则,实现路由场景验证和控制器场景验证。
第一步
创建 app\Http\Requests\SceneValidator.php
trait
<?php
namespace App\Http\Requests;
trait SceneValidator
{
//场景
protected $scene = null;
//是否自动验证
protected $autoValidate = true;
protected $onlyRule=[];
/**
* 覆盖 ValidatesWhenResolvedTrait 下 validateResolved 自动验证
*/
public function validateResolved()
{
if(method_exists($this,'autoValidate')){
$this->autoValidate = $this->container->call([$this, 'autoValidate']);
}
if ($this->autoValidate) {
$this->handleValidate();
}
}
/**
* 复制 ValidatesWhenResolvedTrait -> validateResolved 自动验证
*/
protected function handleValidate()
{
$this->prepareForValidation();
if (! $this->passesAuthorization()) {
$this->failedAuthorization();
}
$instance = $this->getValidatorInstance();
if ($instance->fails()) {
$this->failedValidation($instance);
}
}
/**
* 定义 getValidatorInstance 下 validator 验证器
* @param $factory
* @return mixed
*/
public function validator($factory)
{
return $factory->make($this->validationData(), $this->getRules(), $this->messages(), $this->attributes());
}
/**
* 验证方法(关闭自动验证时控制器调用)
* @param string $scene 场景名称 或 验证规则
*/
public function validate($scene='')
{
if(!$this->autoValidate){
if(is_array($scene)){
$this->onlyRule = $scene;
}else{
$this->scene = $scene;
}
$this->handleValidate();
}
}
/**
* 获取 rules
* @return array
*/
protected function getRules()
{
return $this->handleScene($this->container->call([$this, 'rules']));
}
/**
* 场景验证
* @param array $rule
* @return array
*/
protected function handleScene(array $rule)
{
if($this->onlyRule){
return $this->handleRule($this->onlyRule,$rule);
}
$sceneName = $this->getSceneName();
if($sceneName && method_exists($this,'scene')){
$scene = $this->container->call([$this, 'scene']);
if(array_key_exists($sceneName,$scene)) {
return $this->handleRule($scene[$sceneName],$rule);
}
}
return $rule;
}
/**
* 处理Rule
* @param $sceneRule
* @param $rule
* @return array
*/
private function handleRule(array $sceneRule,array $rule)
{
$rules = [];
foreach ($sceneRule as $key => $value) {
if (is_numeric($key) && array_key_exists($value,$rule)) {
$rules[$value] = $rule[$value];
} else {
$rules[$key] = $value;
}
}
return $rules;
}
/**
* 获取场景名称
*
* @return string
*/
protected function getSceneName()
{
return is_null($this->scene) ? $this->route()->getAction('_scene') : $this->scene;
}
}
第二步
在 app\Providers\AppServiceProvider.php
boot 方法里面添加
use Illuminate\Routing\Route;
//Route 路由自定义scene(场景方法)
Route::macro('scene',function ($scene=null){
$action = Route::getAction();
$action['_scene'] = $scene;
Route::setAction($action);
});
该自定义方法用于路由场景验证,在 Route->action
增加一个 _scene
属性;用法跟 路由别名 name
一样:
Route::post('add','UserController@add')->scene('add');
Tip:如果不需要路由场景验证,则不用添加。
用法
在验证类内里 use SceneValidator
;
例如下面 UserRequest 验证类
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class UserRequest extends FormRequest
{
use SceneValidator;
/**
* 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 [
'name' => 'required|string|unique:users',
'email' => 'required|email|unique:users',
];
}
/**
* 场景规则
* @return array
*/
public function scene()
{
return [
//add 场景
'add' => [
'name' , //复用 rules() 下 name 规则
'email' => 'email|unique:users' //重置规则
],
//edit场景
'edit' => ['name'],
];
}
}
场景规则
在验证类里面添加scene
方法
/**
* 场景规则
* @return array
*/
public function scene()
{
// 格式 ['场景名' => [规则]]
return [
//add 场景
'add' => [
'name', //复用 rules() 下 name 规则
'email' => 'email|unique:users' //重置规则
],
//edit场景
'edit' => ['name'],
];
}
自动验证控制
在验证类里面添加 autoValidate
方法来控制自动验证; true:开启 | false:关闭
默认开启。
/**
* 设置是否自动验证
* @return bool
*/
public function autoValidate(){
return false; //关闭
}
可以根据不同需求开启和关闭灵活控制(此处要配合路由场景验证有效;控制器场景验证则必须关闭自动验证);比如某些场景下关闭
public function autoValidate(){
$sceneName = $this->getSceneName();
if(in_array($sceneName,['add'])){ //add 场景下关闭自动验证
return false;
}
}
路由场景验证
Route::post('create','UserController@create');
Route::post('add','TestController@add')->scene('add');
控制器场景验证
Tip:控制器场景验证需要 关闭自动验证 ,通过
validate('场景名')
来调起验证;优先级: 控制器场景验证 > 路由场景验证
public function add(UserRequest $request){
$request->validate('add');
}
支持全部验证 、独立验证规则
//全部验证 rules()下规则
$request->validate();
// 独立传验证规则
$request->validate( [
'name', //复用 rules() 下 name 规则
'email' => 'email|unique:users' //重置规则
]);
一直没有找到自己理想的场景验证,于是整理了这套场景验证,供参考;
如有更好的场景验证,求分享学习下;
由于第一次写有不足的地方,请各位谅解!
本作品采用《CC 协议》,转载必须注明作者和本文链接
本帖由系统于 3年前 自动加精
:+1:
:+1:
请问laravel7 能用吗? 我试了没有走验证
我按照上面的一比一搞 为啥就是不走场景呢?
我用的是CeRequest 没有用UserRequest 大佬
@echoki
关闭自动验证是 autoValidate() 方法 不是 authorize()
@echoki
@echoki CeRequest 里面加上
嗯嗯 解决了,还是大佬细心 文档写的很好 是我自己不够细心 再次感谢教导 感谢
重写验证完美解决了 同一字段,不同场景对应不同验证规则 的问题
可以根据不同需求开启和关闭灵活控制;比如某些场景下关闭
您上面的场景不生效,我是通过控制器来验证
autoValidate()获取不到场景名称
不知道路由是否生效,没有测试
我是通过路由验证的,一直不生效,laravel8.5
路由配置
控制器
验证场景
配置AppServiceProvider
测试效果,没有验证生效
请问 laravel 5.5 可以用吗?我走了一遍控制器场景验证流程,没有生效
@黎明 看了5.5 ValidatesWhenResolvedTrait 函数名变了 并且与 SceneValidator 函数有冲突 解决办法
大佬,我想知道这个验证的原理(或者让我知道该看哪些文档),这个困扰我很久了。我是会用,但是那样不妥。
还有想向大佬请教下,我该怎么学习这个框架,我自己摸索的2个月,感觉毫无头绪
@NoTurningBack 多写代码,不会就查文档,平时多看看框架的底层代码,学习别人思路,一步一步来
好,谢谢大佬。抱歉这么晚回复你,因为没有关注。关于源码方向,您有什么指教的吗?
之前我看过竟然没有留言,不科学啊 还好又找到了,感谢楼主大大的分享
看评论有好多说没有走验证流程的,在控制器的方法中注入对应的Request即可
这个在验证失败的时候是跳转页面,如何不跳转而返回错误消息 以便api使用呢
感谢分享!!