From 45d439e98a6b14afde8911f7d22a265948adbf72 Mon Sep 17 00:00:00 2001 From: Mohamed Said Date: Sat, 10 Jul 2021 18:26:12 +0200 Subject: [PATCH] [8.x] Add Validator::excludeArrays() to exclude array keys that aren't included in the validation rules (#37943) * allow excluding other array attributes that werent validated * more tests * use preg_quote * formatting * add another test Co-authored-by: Taylor Otwell --- src/Illuminate/Validation/Factory.php | 19 +++++++ src/Illuminate/Validation/Validator.php | 15 +++++- tests/Validation/ValidationValidatorTest.php | 56 ++++++++++++++++++++ 3 files changed, 89 insertions(+), 1 deletion(-) diff --git a/src/Illuminate/Validation/Factory.php b/src/Illuminate/Validation/Factory.php index e9f75d738803..3d9d19035559 100755 --- a/src/Illuminate/Validation/Factory.php +++ b/src/Illuminate/Validation/Factory.php @@ -66,6 +66,13 @@ class Factory implements FactoryContract */ protected $fallbackMessages = []; + /** + * Indicates that unvalidated array keys should be excluded, even if the parent array was validated. + * + * @var bool + */ + protected $excludeUnvalidatedArrayKeys; + /** * The Validator resolver instance. * @@ -115,6 +122,8 @@ public function make(array $data, array $rules, array $messages = [], array $cus $validator->setContainer($this->container); } + $validator->excludeUnvalidatedArrayKeys = $this->excludeUnvalidatedArrayKeys; + $this->addExtensions($validator); return $validator; @@ -239,6 +248,16 @@ public function replacer($rule, $replacer) $this->replacers[$rule] = $replacer; } + /** + * Indicate that unvalidated array keys should be excluded, even if the parent array was validated. + * + * @return void + */ + public function excludeUnvalidatedArrayKeys() + { + $this->excludeUnvalidatedArrayKeys = true; + } + /** * Set the Validator instance resolver. * diff --git a/src/Illuminate/Validation/Validator.php b/src/Illuminate/Validation/Validator.php index b6917ca36eb6..9c64aa4e42af 100755 --- a/src/Illuminate/Validation/Validator.php +++ b/src/Illuminate/Validation/Validator.php @@ -156,6 +156,13 @@ class Validator implements ValidatorContract */ protected $stopOnFirstFailure = false; + /** + * Indicates that unvalidated array keys should be excluded, even if the parent array was validated. + * + * @var bool + */ + public $excludeUnvalidatedArrayKeys = false; + /** * All of the custom validator extensions. * @@ -508,7 +515,13 @@ public function validated() $missingValue = new stdClass; - foreach (array_keys($this->getRules()) as $key) { + foreach ($this->getRules() as $key => $rules) { + if ($this->excludeUnvalidatedArrayKeys && + in_array('array', $rules) && + ! empty(preg_grep('/^'.preg_quote($key, '/').'\.*/', array_keys($this->implicitAttributes)))) { + continue; + } + $value = data_get($this->getData(), $key, $missingValue); if ($value !== $missingValue) { diff --git a/tests/Validation/ValidationValidatorTest.php b/tests/Validation/ValidationValidatorTest.php index 38926cb277fb..931938717f76 100755 --- a/tests/Validation/ValidationValidatorTest.php +++ b/tests/Validation/ValidationValidatorTest.php @@ -6146,6 +6146,62 @@ public function testExcludeIfWhenValidationFails($rules, $data, $expectedMessage $this->assertSame($expectedMessages, $validator->messages()->toArray()); } + public function testExcludingArrays() + { + $validator = new Validator( + $this->getIlluminateArrayTranslator(), + ['users' => [['name' => 'Mohamed', 'location' => 'cairo']]], + ['users' => 'array', 'users.*.name' => 'string'] + ); + $this->assertTrue($validator->passes()); + $this->assertSame(['users' => [['name' => 'Mohamed', 'location' => 'cairo']]], $validator->validated()); + + $validator = new Validator( + $this->getIlluminateArrayTranslator(), + ['users' => [['name' => 'Mohamed', 'location' => 'cairo']]], + ['users' => 'array', 'users.*.name' => 'string'] + ); + $validator->excludeUnvalidatedArrayKeys = true; + $this->assertTrue($validator->passes()); + $this->assertSame(['users' => [['name' => 'Mohamed']]], $validator->validated()); + + $validator = new Validator( + $this->getIlluminateArrayTranslator(), + ['users' => [['name' => 'Mohamed', 'location' => 'cairo']]], + ['users' => 'array'] + ); + $validator->excludeUnvalidatedArrayKeys = true; + $this->assertTrue($validator->passes()); + $this->assertSame(['users' => [['name' => 'Mohamed', 'location' => 'cairo']]], $validator->validated()); + + $validator = new Validator( + $this->getIlluminateArrayTranslator(), + ['users' => ['mohamed', 'zain']], + ['users' => 'array', 'users.*' => 'string'] + ); + $validator->excludeUnvalidatedArrayKeys = true; + $this->assertTrue($validator->passes()); + $this->assertSame(['users' => ['mohamed', 'zain']], $validator->validated()); + + $validator = new Validator( + $this->getIlluminateArrayTranslator(), + ['users' => ['admins' => [['name' => 'mohamed', 'job' => 'dev']]]], + ['users' => 'array', 'users.admins' => 'array', 'users.admins.*.name' => 'string'] + ); + $validator->excludeUnvalidatedArrayKeys = true; + $this->assertTrue($validator->passes()); + $this->assertSame(['users' => ['admins' => [['name' => 'mohamed']]]], $validator->validated()); + + $validator = new Validator( + $this->getIlluminateArrayTranslator(), + ['users' => [1, 2, 3]], + ['users' => 'array|max:10'] + ); + $validator->excludeUnvalidatedArrayKeys = true; + $this->assertTrue($validator->passes()); + $this->assertSame(['users' => [1, 2, 3]], $validator->validated()); + } + public function testExcludeUnless() { $validator = new Validator(