来!实现一个表单场景验证
有时候觉得每个验证都要写个文件,有时候会有大量的重复字段,总想偷个懒,感觉tp的场景验证还是可以借鉴的。
1、先让所有的验证文件都继承 BaseRequest;BaseRequest再继承FormRequest;
2、然后在 BaseRequest 内改写 createDefaultValidator 方法;
protected function createDefaultValidator(ValidationFactory $factory)
{
$rules = method_exists($this, 'rules')
?
(method_exists($this, 'scene') ? $this->container->call([
$this, 'getRules',
]) : $this->container->call([$this, 'rules']))
:
[];
$validator = $factory->make(
$this->validationData(), $rules,
$this->messages(), $this->attributes(),
)->stopOnFirstFailure($this->stopOnFirstFailure);
if ($this->isPrecognitive()) {
$validator->setRules(
$this->filterPrecognitiveRules($validator->getRulesWithoutPlaceholders()),
);
}
return $validator;
}
这里有验证规则获取的是 getRules() 方法;
3、添加 getRules() 方法;
public function getRules()
{
$rules = $this->container->call([$this, 'rules']);
$scene = $this->container->call([$this, 'scene']);
$action = request()->route()->getActionName();
[$class, $methodName] = explode('@', $action);
$rulesArr = [];
if (!isset($scene[$methodName])) {
return $rules;
}
foreach ($scene[$methodName] as $sceneKey => $sceneItem) {
if (is_int($sceneKey) && isset($rules[$sceneItem])) {
$rulesArr[$sceneItem] = $rules[$sceneItem];
}
if (is_string($sceneKey) && isset($rules[$sceneKey])) {
$rulesArr[$sceneKey] = $sceneItem;
}
}
return $rulesArr;
}
这里是获取路由访问的方法名,根据方法名来获取场景对应的验证数组;
4、是具体的验证文件里添加以方法名为场景的方法 scene();
public function scene(): array
{
return [
'login' => ['email', 'password'],
'register' => ['email', 'password', 'name'],
'miniapp_login' => ['code'],
];
}
5、最后上完整代码;
UserRegisterRequest.php;重新failedValidation()方法,只是为了接口返回一条错误信息就可以了,和本次要说的场景验证关系不大;
<?php
namespace App\Api\Requests;
use Illuminate\Contracts\Validation\Factory as ValidationFactory;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Http\Exceptions\HttpResponseException;
class BaseRequest extends FormRequest
{
protected function failedValidation($validator)
{
$error = $validator->errors()->all();
throw new HttpResponseException(fail([600000, $error['0']]));
}
protected function createDefaultValidator(ValidationFactory $factory)
{
$rules = method_exists($this, 'rules')
?
(method_exists($this, 'scene') ? $this->container->call([
$this, 'getRules',
]) : $this->container->call([$this, 'rules']))
:
[];
$validator = $factory->make(
$this->validationData(), $rules,
$this->messages(), $this->attributes(),
)->stopOnFirstFailure($this->stopOnFirstFailure);
if ($this->isPrecognitive()) {
$validator->setRules(
$this->filterPrecognitiveRules($validator->getRulesWithoutPlaceholders()),
);
}
return $validator;
}
public function getRules()
{
$rules = $this->container->call([$this, 'rules']);
$scene = $this->container->call([$this, 'scene']);
$action = request()->route()->getActionName();
[$class, $methodName] = explode('@', $action);
$rulesArr = [];
if (!isset($scene[$methodName])) {
return $rules;
}
foreach ($scene[$methodName] as $sceneKey => $sceneItem) {
if (is_int($sceneKey) && isset($rules[$sceneItem])) {
$rulesArr[$sceneItem] = $rules[$sceneItem];
}
if (is_string($sceneKey) && isset($rules[$sceneKey])) {
$rulesArr[$sceneKey] = $sceneItem;
}
}
return $rulesArr;
}
public function messages(): array
{
return [
'required' => '缺少参数::attribute',
'numeric' => ':attribute 必须为数字',
'email' => '邮箱格式不正确',
'integer' => ':attribute 必须为整数',
'gt' => ':attribute 取值范围错误',
'gte' => ':attribute 必须大于等于:value',
'max' => ':attribute 最多:max个字符',
'min' => ':attribute 最少:min个字符',
'alpha_dash' => ':attribute 只能为字母、数字、短破折号、下划线',
'in' => ':attribute 取值范围只能 :values',
'unique' => ':attribute 已存在',
'string' => ':attribute 格式错误',
'regex' => ':attribute 格式错误',
'exists' => ':attribute 信息不存在',
'date_format' => ':attribute 格式为不正确',
'after_or_equal' => ':attribute 必须大于等于 :date',
];
}
}
PostRequest.php
<?php
namespace App\Api\Requests;
class PostRequest extends BaseRequest
{
// 验证规则
public function rules(): array
{
return [
'email' => 'required|email',
'password' => 'required|min:6|max:16',
'code' => 'required',
'name' => 'required|string|min:2|max:10',
];
}
public function attributes(): array
{
return [
'name' => '姓名',
'email' => '邮箱',
'password' => '密码',
'code' => 'code',
];
}
// 实现场景验证,key对应控制器里的方法名;
public function scene(): array
{
return [
'login' => ['email', 'password'],
'store' => ['email', 'password', 'name'],
'miniapp_login' => ['code'],
];
}
// 还可以重新定义规则
// public function scene(): array
// {
// return [
// 'login' => [
// 'email' => 'required|unique|email',
// 'password' => 'required|min:6|max:16',
// ],
// 'register' => ['email', 'password', 'name'],
// 'miniapp_login' => ['code'],
// ];
// }
}
6、最后在控制器内使用;
/**
* 新增
* @param ArticleRequest $request
* @return JsonResponse
* @throws BusinessException
*/
public function store(PostRequest $request): JsonResponse
{
}
到此 完成了我想要的目的。
各位大佬,有更好的想法还请不要吝啬交流。
本作品采用《CC 协议》,转载必须注明作者和本文链接
:joy: 我还是始终觉得明确单一职责是没啥问题的。你这个简单的还好,如果遇到复杂的场景,这个应付不过来。
心智负担太高了,如果真的有重复的情况,可以通过 extends 或者 trait 来解决,我觉得这样解决不是一个优雅的方式。
叠甲: 无恶意,一个东西有多种实现方式,没有绝对的对与错
如果直接继承之前相同表单的验证器,然后重写其中的方法会不会更优雅一点
1、场景使用复杂、阅读不方便、浪费心智,不如分开写多个验证类简单。
2、压根不实用,比如创建数据的时候要验证title唯一,修改的时候title唯一并且排除当前修改行数据,你的场景压根无法解决这种问题