laravel - 验证数组 - 获取当前迭代
问题描述
我正在尝试使用 Laravel 的FormRequest
.
客户正在提交一个订单,其中包含一系列商品。我们要求用户仅在和special_delivery
时才表明该项目是否需要。asking_price > 500
quantity > 10
以下是我的预期规则:
public function rules() {
'customer_id' => 'required|integer|exists:customers,id',
'items' => 'required|array',
'items.*.name' => 'required|string',
'items.*.asking_price' => 'required|numeric',
'items.*.quantity' => 'required|numeric',
'items.*.special_delivery' // required if price > 500 && quantity > 10
}
我试图按照这些思路做一些事情:
Rule::requiredIf($this->input('item.*.asking_price') > 500 && $this->input('item.*.quantity' > 10));
这样做的问题是我找不到访问当前items
迭代索引以指示要验证哪个项目的方法。
我还尝试了以下自定义验证:
function ($attribute, $value, $fail) {
preg_match('/\d+/', $attribute, $m);
$askingPrice = $this->input('items')[$m[0]]['asking_price'];
$quantity= $this->input('items')[$m[0]]['quantity'];
if ($askingPrice > 500 && $quantity > 10) {
$fail("$attribute is required");
}
}
虽然这个函数让我可以访问当前$attribute
的,但问题是它只有在存在时才会运行special_delivery
。这违背了整个目的!
任何帮助都感激不尽!谢谢!
解决方案
我可能已经想出了一个解决你的问题的方法,sometimes
如果你愿意的话,一个索引知道。
由于很遗憾无法将宏添加到验证器,您要么必须覆盖验证工厂(这就是我的建议)并使用您自己的自定义验证类,要么根据该方法创建一个辅助函数,将验证器实例作为附加参数并使用 this 而不是$this
.
酱汁第一:indexAwareSometimes
验证函数
function indexAwareSometimes(
\Illuminate\Contracts\Validation\Validator $validator,
string $parent,
$attribute,
$rules,
\Closure $callback
) {
foreach (Arr::get($validator->getData(), $parent) as $index => $item) {
if ($callback($validator->getData(), $index)) {
foreach ((array) $attribute as $key) {
$path = $parent.'.'.$index.'.'.$key;
$validator->addRules([$path => $rules]);
}
}
}
}
很多灵感显然来自sometimes
方法,并没有太大变化。我们基本上是遍历包含所有其他数组 ( ) 的数组($parent
在您的情况下为数组),其中包含要验证的实际数据,如果评估为真,则将( ) 添加到当前索引中的( )。items
items.*
$rules
required
$attribute
special_delivery
$callback
回调闭包需要两个参数,第一个是$data
父验证实例的形式,由 检索Validator::getData()
,第二个$index
是外部foreach
在它调用回调时的形式。
在您的情况下,该函数的用法看起来有点像这样:
use Illuminate\Support\Arr;
class YourFormRequest extends FormRequest
{
public function rules()
{
return [
'customer_id' => 'required|integer|exists:customers,id',
'items' => 'required|array',
'items.*.name' => 'required|string',
'items.*.asking_price' => 'required|numeric',
'items.*.quantity' => 'required|numeric',
];
}
public function getValidatorInstance()
{
$validator = parent::getValidatorInstance();
indexAwareSometimes(
$validator,
'items',
'special_delivery',
'required',
fn ($data, $index) => Arr::get($data, 'items.'.$index.'.asking_price') > 500 &&
Arr::get($data, 'items.'.$index.'.quantity') > 10
);
}
}
扩展原生Validator
类
扩展 Laravel 的原生Validator 类并不像听起来那么难。我们正在创建一个自定义的 ValidationServiceProvider 并继承 LaravelIlluminate\Validation\ValidationServiceProvider
作为父级。只有registerValidationFactory
方法需要被它的副本替换,我们指定了工厂应该使用的自定义验证器解析器:
<?php
namespace App\Providers;
use App\Validation\CustomValidator;
use Illuminate\Contracts\Translation\Translator;
use Illuminate\Validation\Factory;
use Illuminate\Validation\ValidationServiceProvider as ParentValidationServiceProvider;
class ValidationServiceProvider extends ParentValidationServiceProvider
{
protected function registerValidationFactory(): void
{
$this->app->singleton('validator', function ($app) {
$validator = new Factory($app['translator'], $app);
$resolver = function (
Translator $translator,
array $data,
array $rules,
array $messages = [],
array $customAttributes = []
) {
return new CustomValidator($translator, $data, $rules, $messages, $customAttributes);
};
$validator->resolver($resolver);
if (isset($app['db'], $app['validation.presence'])) {
$validator->setPresenceVerifier($app['validation.presence']);
}
return $validator;
});
}
}
自定义验证器继承了 Laravel 的Illuminate\Validation\Validator
并添加了indexAwareSometimes
方法:
<?php
namespace App\Validation;
use Closure;
use Illuminate\Support\Arr;
use Illuminate\Validation\Validator;
class CustomValidator extends Validator
{
/**
* @param string $parent
* @param string|array $attribute
* @param string|array $rules
* @param Closure $callback
*/
public function indexAwareSometimes(string $parent, $attribute, $rules, Closure $callback)
{
foreach (Arr::get($this->data, $parent) as $index => $item) {
if ($callback($this->data, $index)) {
foreach ((array) $attribute as $key) {
$path = $parent.'.'.$index.'.'.$key;
$this->addRules([$path => $rules]);
}
}
}
}
}
然后我们只需要用Illuminate\Validation\ValidationServiceProvider
你自己的自定义服务提供商替换 Laravelconfig/app.php
就可以了。
它甚至适用于Barry vd。Heuvel 的laravel-ide-helper
包裹。
return [
'providers' => [
//Illuminate\Validation\ValidationServiceProvider::class,
App\Providers\ValidationServiceProvider::class,
]
]
回到上面的例子,你只需要改变getValidatorInstance()
你的表单请求的方法:
public function getValidatorInstance()
{
$validator = parent::getValidatorInstance();
$validator->indexAwareSometimes(
'items',
'special_delivery',
'required',
fn ($data, $index) => Arr::get($data, 'items.'.$index.'.asking_price') > 500 &&
Arr::get($data, 'items.'.$index.'.quantity') > 10
);
}
推荐阅读
- jmeter - JMeter - 仅在非 GUI 中忽略查看结果树侦听器
- c++ - 多次调用时,`std::future::then` 的行为是什么?
- c# - C# Visual Studio:向 localdb 添加列重置所有数据
- cordova - 在加载当前加载现有 IONIC 项目的当前房屋之前建立新的初始视图以加载
- ruby - git push 和 jekyll build for jekyll blog 有什么区别
- docker - 詹金斯无法连接守护进程
- python - 如何使用 python 读取 .odt?
- ios - 使用本地通知 api 在 swift 3 中触发每周通知
- twitter-bootstrap - 如何使用 Bootstrap 3 或 4 - 16 & 24 网格系统
- spring-boot - springboot2.0中使用@cacheable时如何为每个redis缓存配置不同的ttl