diff --git a/src/Illuminate/Validation/Validator.php b/src/Illuminate/Validation/Validator.php index 1daa8aa3963c..1a0d076609cf 100755 --- a/src/Illuminate/Validation/Validator.php +++ b/src/Illuminate/Validation/Validator.php @@ -1125,6 +1125,30 @@ public function sometimes($attribute, $rules, callable $callback) return $this; } + /** + * Add conditions to a given nested field based on a Closure. + * + * @param string $parent + * @param string|array $attribute + * @param string|array $rules + * @param callable $callback + * @return $this + */ + public function sometimesNested(string $parent, $attribute, $rules, callable $callback) + { + foreach ((array) $this->getValue($parent) as $index => $item) { + $payload = new Fluent($item); + + if ($callback($payload)) { + foreach ((array) $attribute as $key) { + $this->addRules([$parent.'.'.$index.'.'.$key => $rules]); + } + } + } + + return $this; + } + /** * Instruct the validator to stop validating after the first rule failure. * diff --git a/tests/Validation/ValidationValidatorTest.php b/tests/Validation/ValidationValidatorTest.php index d6312081068c..3f11b08aff54 100755 --- a/tests/Validation/ValidationValidatorTest.php +++ b/tests/Validation/ValidationValidatorTest.php @@ -4129,6 +4129,109 @@ public function testSometimesAddingRules() $this->assertEquals(['foo.0.name' => ['Required', 'String']], $v->getRules()); } + public function testSometimesNestedAddingRules() + { + $data = [ + 'first' => [ + [ + 'type' => 'email', + 'value' => 'test@test.com', + 'description' => 'Foo Bar', + ], + [ + 'type' => 'email', + 'value' => 'No valid email', + 'description' => 'Foo Bar', + ], + [ + 'type' => 'url', + 'value' => 'https://www.laraval.com/', + 'description' => 'Foo Bar', + ], + [ + 'type' => 'url', + 'value' => 'No valid URL', + 'description' => 'Foo Bar', + ], + [ + 'type' => 'string', + 'value' => 'Valid string', + 'description' => 'Foo Bar', + ], + ], + 'second' => [ + 'deep' => [ + [ + 'type' => 'email', + 'value' => 'test@test.com', + 'description' => 'Foo Bar', + ], + [ + 'type' => 'string', + 'value' => 'Valid string', + 'description' => 'Foo Bar', + ], + ], + ], + ]; + + $trans = $this->getIlluminateArrayTranslator(); + $v = new Validator($trans, $data, ['first.2.value' => ['Required'], 'first.4.value' => ['Required']]); + $v->sometimesNested('first', 'value', 'String', function ($i) { + return $i->type === 'url'; + }); + $this->assertNotEquals(['first.4.value' => ['Required', 'String']], $v->getRules()); + + $trans = $this->getIlluminateArrayTranslator(); + $v = new Validator($trans, $data, ['first.4.value' => ['Required']]); + $v->sometimesNested('first', 'value', 'String', function ($i) { + return $i->type === 'string'; + }); + $this->assertEquals(['first.4.value' => ['Required', 'String']], $v->getRules()); + + $trans = $this->getIlluminateArrayTranslator(); + $v = new Validator($trans, $data, ['first.4.value' => ['Required']]); + $v->sometimesNested('first', 'value', 'email:rfc,dns', function ($i) { + return $i->type === 'email'; + }); + $this->assertNotEquals(['first.4.value' => ['Required', 'String']], $v->getRules()); + + $trans = $this->getIlluminateArrayTranslator(); + $v = new Validator($trans, $data, ['first.0.value' => ['Required']]); + $v->sometimesNested('first', 'value', 'email:rfc,dns', function ($i) { + return $i->type === 'email'; + }); + $this->assertNotEquals(['first.4.value' => ['Required', 'String']], $v->getRules()); + + $trans = $this->getIlluminateArrayTranslator(); + $v = new Validator($trans, $data, ['first.0.value' => ['Required']]); + $v->sometimesNested('first', 'value', 'email:rfc,dns', function ($i) { + return $i->type === 'email'; + }); + $this->assertNotEquals(['first.4.value' => ['Required', 'String']], $v->getRules()); + + $trans = $this->getIlluminateArrayTranslator(); + $v = new Validator($trans, $data, ['first.4.value' => ['Required']]); + $v->sometimesNested('first', 'value', 'email:rfc,dns', function ($i) { + return $i->type === 'email'; + }); + $this->assertEquals(['first.0.value' => ['email:rfc,dns'], 'first.1.value' => ['email:rfc,dns'], 'first.4.value' => ['Required']], $v->getRules()); + + $trans = $this->getIlluminateArrayTranslator(); + $v = new Validator($trans, $data, ['first.0.type' => ['Required']]); + $v->sometimesNested('first', 'value', 'email:rfc,dns', function ($i) { + return true; + }); + $this->assertNotEquals(['first.0.type' => ['Required'], 'first.4.type' => ['email:rfc,dns']], $v->getRules()); + + $trans = $this->getIlluminateArrayTranslator(); + $v = new Validator($trans, $data, ['second.deep.0.value' => ['Required'], 'second.deep.1.value' => ['Required']]); + $v->sometimesNested('second.deep', 'value', 'email:rfc,dns', function ($i) { + return $i->type === 'email'; + }); + $this->assertEquals(['second.deep.0.value' => ['Required', 'email:rfc,dns'], 'second.deep.1.value' => ['Required']], $v->getRules()); + } + public function testCustomValidators() { $trans = $this->getIlluminateArrayTranslator();