嵌套数组验证 laravel
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/34393279/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Nested array validation laravel
提问by Ajeesh
I am building a REST based API, where one of the API is having the following request
我正在构建一个基于 REST 的 API,其中一个 API 有以下请求
{
"categories_id" :"1",
"product_name" : "Pen",
"product_description" : "this is pen",
"tags" : "pen,write",
"image_count" : "4",
"skus":
{
"is_shippable":"n",
"actual_price":"100.55",
"selling_price":"200.45",
"quantity_type":"bucket",
"quantity_total":"10",
"bucket_value":"instock",
"sort_order":"1"
}
}
These are my validation rules
这些是我的验证规则
protected $rules = [
ValidatorInterface::RULE_CREATE => [
'users_id' => 'required',
'user_profiles_id' => 'required',
'categories_id' => 'required',
'product_name' => 'required|max:100',
'product_description' => 'required|max:1000',
'tags' => 'required',
'image_count'=>'required|integer',
'creation_mode'=>'required|integer',
'skus.is_shippable'=>'in:y,n',
'skus.actual_price'=>'regex:/^\s*(?=.*[1-9])\d*(?:\.\d{1,2})?\s*$/',
'skus.selling_price' => 'regex:/^\s*(?=.*[1-9])\d*(?:\.\d{1,2})?\s*$/',
'skus.quantity_type' => 'sometimes|required|in:finite,infinite,bucket',
'skus.quantity_total' => 'integer|required_if:skus.quantity_type,finite',
'skus.bucket_value'=>'in:instock,soldout,limited|required_if:skus.quantity_type,bucket',
'skus.sort_order'=> 'required|integer'
],
ValidatorInterface::RULE_UPDATE => [
]
];
The above request is properly getting validated. But The skus can have multiple entities inside like below request
上述请求已正确得到验证。但是 skus 内部可以有多个实体,如下面的请求
{
"categories_id" :"1",
"product_name" : "Pen",
"product_description" : "this is pen",
"tags" : "pen,write",
"image_count" : "4",
"skus":
[{
"is_shippable":"n",
"actual_price":"100.55",
"selling_price":"200.45",
"quantity_type":"bucket",
"quantity_total":"10",
"bucket_value":"instock",
"sort_order":"1"
},
{
"is_shippable":"n",
"actual_price":"100.55",
"selling_price":"200.45",
"quantity_type":"bucket",
"quantity_total":"10",
"bucket_value":"instock",
"sort_order":"1"
}]
}
How do I validate if there are multiple nested entities?
如何验证是否有多个嵌套实体?
回答by Fabio Antunes
What version of Laravel are you using? If you are using Laravel 5.2or if you don't mind updating to it, there's a solution out of the box.
你用的是什么版本的 Laravel?如果您使用的是Laravel 5.2或者您不介意更新到它,那么这里有一个开箱即用的解决方案。
Array Validation
Validating array form input fields is much easier in Laravel 5.2. For example, to validate that each e-mail in a given array input field is unique, you may do the following:
数组验证
在 Laravel 5.2 中验证数组表单输入字段要容易得多。例如,要验证给定数组输入字段中的每封电子邮件都是唯一的,您可以执行以下操作:
$validator = Validator::make($request->all(), [
'person.*.email' => 'email|unique:users'
]);
Likewise, you may use the * character when specifying your validation messages in your language files, making it a breeze to use a single validation message for array based fields:
同样,您可以在语言文件中指定验证消息时使用 * 字符,这使得对基于数组的字段使用单个验证消息变得轻而易举:
'custom' => [
'person.*.email' => [
'unique' => 'Each person must have a unique e-mail address',
]
],
Another example from Laravel news:
另一个来自Laravel 新闻的例子:
Pretend you have a form with an array of input fields like this:
假设您有一个包含一系列输入字段的表单,如下所示:
<p>
<input type="text" name="person[1][id]">
<input type="text" name="person[1][name]">
</p>
<p>
<input type="text" name="person[2][id]">
<input type="text" name="person[2][name]">
</p>
In Laravel 5.1 to add validation rules it required looping through and adding the rules individually. Instead of having to do all that it's been “Laravelized” into this:
在 Laravel 5.1 中添加验证规则需要循环并单独添加规则。不必做所有已经“Laravelized”的事情:
$v = Validator::make($request->all(), [
'person.*.id' => 'exists:users.id',
'person.*.name' => 'required:string',
]);
So if you don't want to use Laravel 5.2 you will have to do it manually, if you do update to Laravel 5.2 you can use the new array validation and it will be some like this:
因此,如果您不想使用 Laravel 5.2,则必须手动进行,如果您更新到 Laravel 5.2,则可以使用新的数组验证,它会是这样的:
protected $rules = [
ValidatorInterface::RULE_CREATE => [
'users_id' => 'required',
'user_profiles_id' => 'required',
'categories_id' => 'required',
'product_name' => 'required|max:100',
'product_description' => 'required|max:1000',
'tags' => 'required',
'image_count'=>'required|integer',
'creation_mode'=>'required|integer',
'skus.*.is_shippable'=>'in:y,n',
'skus.*.actual_price'=>'regex:/^\s*(?=.*[1-9])\d*(?:\.\d{1,2})?\s*$/',
'skus.*.selling_price' => 'regex:/^\s*(?=.*[1-9])\d*(?:\.\d{1,2})?\s*$/',
'skus.*.quantity_type' => 'sometimes|required|in:finite,infinite,bucket',
'skus.*.quantity_total' => 'integer|required_if:skus.quantity_type,finite',
'skus.*.bucket_value'=>'in:instock,soldout,limited|required_if:skus.quantity_type,bucket',
'skus.*.sort_order'=> 'required|integer'
],
ValidatorInterface::RULE_UPDATE => [
]
];
Edit
编辑
Ihmo the best way to add this extra validation logic is extending the Validatorclass creating your CustomValidatorclass, it may be a bit overkill, but when Laravel 5.2 gets released you can remove your CustomValidator and keep on using Laravel's 5.2 Validator without making any changes to your code.
Ihmo 添加此额外验证逻辑的最佳方法是扩展Validator类创建您的CustomValidator类,这可能有点矫枉过正,但是当 Laravel 5.2 发布时,您可以删除您的 CustomValidator 并继续使用 Laravel 的 5.2 验证器,而无需对你的代码。
How? Well first we create a folder under our app/
I decided to name this folder Validatoryou can name it whatever you want, just remember to update the namespace of the following classes. Next we are going to create 3 .php files inside this folder CustomValidator.php, CustomValidatorServiceProvider.phpand Factory.php.
如何?好吧,首先我们在我们的下创建一个文件夹,app/
我决定将这个文件夹命名为Validator,您可以随意命名,只需记住更新以下类的命名空间即可。接下来,我们将在此文件夹中创建 3 个 .php 文件CustomValidator.php、CustomValidatorServiceProvider.php和Factory.php。
CustomValidator.php
自定义验证器.php
<?php
namespace App\Validator;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use Illuminate\Validation\Validator;
use Symfony\Component\Translation\TranslatorInterface;
class CustomValidator extends Validator
{
/**
* Create a new Validator instance.
*
* @param \Symfony\Component\Translation\TranslatorInterface $translator
* @param array $data
* @param array $rules
* @param array $messages
* @param array $customAttributes
* @return void
*/
public function __construct(TranslatorInterface $translator, array $data, array $rules, array $messages = [], array $customAttributes = [])
{
$this->translator = $translator;
$this->customMessages = $messages;
$this->data = $this->parseData($data);
$this->customAttributes = $customAttributes;
// Explode the rules first so that the implicit ->each calls are made...
$rules = $this->explodeRules($rules);
$this->rules = array_merge((array) $this->rules, $rules);
}
/**
* Explode the rules into an array of rules.
*
* @param string|array $rules
* @return array
*/
protected function explodeRules($rules)
{
foreach ($rules as $key => $rule) {
if (Str::contains($key, '*')) {
$this->each($key, $rule);
unset($rules[$key]);
} else {
$rules[$key] = (is_string($rule)) ? explode('|', $rule) : $rule;
}
}
return $rules;
}
/**
* Define a set of rules that apply to each element in an array attribute.
*
* @param string $attribute
* @param string|array $rules
* @return void
*
* @throws \InvalidArgumentException
*/
public function each($attribute, $rules)
{
$data = Arr::dot($this->data);
foreach ($data as $key => $value) {
if (Str::startsWith($key, $attribute) || Str::is($attribute, $key)) {
foreach ((array) $rules as $ruleKey => $ruleValue) {
if (! is_string($ruleKey) || Str::endsWith($key, $ruleKey)) {
$this->mergeRules($key, $ruleValue);
}
}
}
}
}
/**
* Get the inline message for a rule if it exists.
*
* @param string $attribute
* @param string $lowerRule
* @param array $source
* @return string|null
*/
protected function getInlineMessage($attribute, $lowerRule, $source = null)
{
$source = $source ?: $this->customMessages;
$keys = ["{$attribute}.{$lowerRule}", $lowerRule];
// First we will check for a custom message for an attribute specific rule
// message for the fields, then we will check for a general custom line
// that is not attribute specific. If we find either we'll return it.
foreach ($keys as $key) {
foreach (array_keys($source) as $sourceKey) {
if (Str::is($sourceKey, $key)) {
return $source[$sourceKey];
}
}
}
}
/**
* Get the custom error message from translator.
*
* @param string $customKey
* @return string
*/
protected function getCustomMessageFromTranslator($customKey)
{
$shortKey = str_replace('validation.custom.', '', $customKey);
$customMessages = Arr::dot(
(array) $this->translator->trans('validation.custom')
);
foreach ($customMessages as $key => $message) {
if ($key === $shortKey || (Str::contains($key, ['*']) && Str::is($key, $shortKey))) {
return $message;
}
}
return $customKey;
}
}
This custom validator has all the changes that were made on Laravel 5.2, you can check them in here
这个自定义验证器包含在 Laravel 5.2 上所做的所有更改,您可以在此处查看
Now since we have a new CustomValidator class we have to find a way of using it, for that we have to extend the ValidatorServiceProviderand the Validator factory.
现在因为我们有了一个新的 CustomValidator 类,我们必须找到一种使用它的方法,为此我们必须扩展ValidatorServiceProvider和Validator factory。
CustomValidatorServiceProvider.php
CustomValidatorServiceProvider.php
<?php
namespace App\Validator;
class CustomValidatorServiceProvider extends \Illuminate\Validation\ValidationServiceProvider
{
/**
* Register the validation factory.
*
* @return void
*/
protected function registerValidationFactory()
{
$this->app->singleton('validator', function ($app) {
$validator = new Factory($app['translator'], $app);
// The validation presence verifier is responsible for determining the existence
// of values in a given data collection, typically a relational database or
// other persistent data stores. And it is used to check for uniqueness.
if (isset($app['validation.presence'])) {
$validator->setPresenceVerifier($app['validation.presence']);
}
return $validator;
});
}
}
Factory.php
工厂.php
<?php
namespace App\Validator;
use App\Validator\CustomValidator as Validator;
class Factory extends \Illuminate\Validation\Factory
{
/**
* Resolve a new Validator instance.
*
* @param array $data
* @param array $rules
* @param array $messages
* @param array $customAttributes
* @return App\Test\CustomValidator
*/
protected function resolve(array $data, array $rules, array $messages, array $customAttributes)
{
if (is_null($this->resolver)) {
return new Validator($this->translator, $data, $rules, $messages, $customAttributes);
}
return call_user_func($this->resolver, $this->translator, $data, $rules, $messages, $customAttributes);
}
}
Now that we have extended our validation to support the nested syntax sku.*.id
现在我们已经扩展了验证以支持嵌套语法 sku.*.id
We just have to swap the Validator to our CustomValidator, and the last step is changing the file config/app.phpand inside the ServiceProviders array look for the ValidatorServiceProvider, just comment that line and add our extended service provider, like this:
我们只需要将 Validator 交换到我们的 CustomValidator,最后一步是更改文件config/app.php并在 ServiceProviders 数组中查找ValidatorServiceProvider,只需注释该行并添加我们的扩展服务提供者,如下所示:
....
// Illuminate\Validation\ValidationServiceProvider::class,
App\Validator\CustomValidatorServiceProvider::class,
....
The reason we are commenting it out is because whenever you update your Laravel 5.1 to 5.2 you just want to uncomment it, remove our CustomValidatorServiceProvider from the list and then you delete our app/Validator folder because we don't need it anymore.
我们将其注释掉的原因是,每当您将 Laravel 5.1 更新到 5.2 时,您只想取消注释,从列表中删除我们的 CustomValidatorServiceProvider,然后删除我们的 app/Validator 文件夹,因为我们不再需要它了。