From d78c0562c050a7c04145e80af151f12912790ef3 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 12 Aug 2021 10:16:14 -0500 Subject: [PATCH 1/5] conditional rules --- .../Validation/ConditionalRules.php | 53 +++++++++++++++++++ src/Illuminate/Validation/Rule.php | 12 +++++ .../Validation/ValidationRuleParser.php | 34 ++++++++++++ src/Illuminate/Validation/Validator.php | 2 +- tests/Validation/ValidationRuleParserTest.php | 28 ++++++++++ 5 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 src/Illuminate/Validation/ConditionalRules.php create mode 100644 tests/Validation/ValidationRuleParserTest.php diff --git a/src/Illuminate/Validation/ConditionalRules.php b/src/Illuminate/Validation/ConditionalRules.php new file mode 100644 index 000000000000..bc9a0739ab41 --- /dev/null +++ b/src/Illuminate/Validation/ConditionalRules.php @@ -0,0 +1,53 @@ +condition = $condition; + $this->rules = $rules; + } + + /** + * Determine if the conditional rules should be added. + * + * @return bool + */ + public function passes() + { + return $this->condition; + } + + /** + * Get the rules. + * + * @return array + */ + public function rules() + { + return is_string($this->rules) ? explode('|', $this->rules) : $this->rules; + } +} diff --git a/src/Illuminate/Validation/Rule.php b/src/Illuminate/Validation/Rule.php index 30cb98dc2b79..f38c09b1524f 100644 --- a/src/Illuminate/Validation/Rule.php +++ b/src/Illuminate/Validation/Rule.php @@ -15,6 +15,18 @@ class Rule { use Macroable; + /** + * Create a new conditional rule set. + * + * @param bool $condition + * @param array|string $rules + * @return \Illuminate\Validation\ConditionalRules + */ + public static function when($condition, $rules) + { + return new ConditionalRules($condition, $rules); + } + /** * Get a dimensions constraint builder instance. * diff --git a/src/Illuminate/Validation/ValidationRuleParser.php b/src/Illuminate/Validation/ValidationRuleParser.php index 8711a2c26837..0af7abfd3d53 100644 --- a/src/Illuminate/Validation/ValidationRuleParser.php +++ b/src/Illuminate/Validation/ValidationRuleParser.php @@ -274,4 +274,38 @@ protected static function normalizeRule($rule) return $rule; } } + + /** + * Expand and conditional rules in the given array of rules. + * + * @param array $rules + * @return array + */ + public static function filterConditionalRules($rules) + { + return collect($rules)->mapWithKeys(function ($attributeRules, $attribute) { + if (! is_array($attributeRules) && + ! $attributeRules instanceof ConditionalRules) { + return [$attribute => $attributeRules]; + } + + if ($attributeRules instanceof ConditionalRules && + $attributeRules->passes()) { + return [$attribute => $attributeRules->rules()]; + } + + if ($attributeRules instanceof ConditionalRules && + ! $attributeRules->passes()) { + return [$attribute => null]; + } + + return [$attribute => collect($attributeRules)->map(function ($rule) { + if (! $rule instanceof ConditionalRules) { + return [$rule]; + } + + return $rule->passes() ? $rule->rules() : null; + })->filter()->flatten(1)->values()->all()]; + })->filter()->all(); + } } diff --git a/src/Illuminate/Validation/Validator.php b/src/Illuminate/Validation/Validator.php index 6fe9a609b87f..3e4a6b975a06 100755 --- a/src/Illuminate/Validation/Validator.php +++ b/src/Illuminate/Validation/Validator.php @@ -1082,7 +1082,7 @@ public function addRules($rules) // of the explicit rules needed for the given data. For example the rule // names.* would get expanded to names.0, names.1, etc. for this data. $response = (new ValidationRuleParser($this->data)) - ->explode($rules); + ->explode(ValidationRuleParser::filterConditionalRules($rules)); $this->rules = array_merge_recursive( $this->rules, $response->rules diff --git a/tests/Validation/ValidationRuleParserTest.php b/tests/Validation/ValidationRuleParserTest.php new file mode 100644 index 000000000000..a3555578e2cf --- /dev/null +++ b/tests/Validation/ValidationRuleParserTest.php @@ -0,0 +1,28 @@ + Rule::when(true, ['required', 'min:2']), + 'email' => Rule::when(false, ['required', 'min:2']), + 'password' => Rule::when(true, 'required|min:2'), + 'username' => ['required', Rule::when(true, ['min:2'])], + 'address' => ['required', Rule::when(false, ['min:2'])], + ]); + + $this->assertEquals([ + 'name' => ['required', 'min:2'], + 'password' => ['required', 'min:2'], + 'username' => ['required', 'min:2'], + 'address' => ['required'], + ], $rules); + } +} From 6ce6e033bdb8ca07882403e1b13443687cea68eb Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 12 Aug 2021 10:22:10 -0500 Subject: [PATCH 2/5] simplify some code --- src/Illuminate/Validation/ValidationRuleParser.php | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/Illuminate/Validation/ValidationRuleParser.php b/src/Illuminate/Validation/ValidationRuleParser.php index 0af7abfd3d53..4a020956b2cd 100644 --- a/src/Illuminate/Validation/ValidationRuleParser.php +++ b/src/Illuminate/Validation/ValidationRuleParser.php @@ -289,14 +289,8 @@ public static function filterConditionalRules($rules) return [$attribute => $attributeRules]; } - if ($attributeRules instanceof ConditionalRules && - $attributeRules->passes()) { - return [$attribute => $attributeRules->rules()]; - } - - if ($attributeRules instanceof ConditionalRules && - ! $attributeRules->passes()) { - return [$attribute => null]; + if ($attributeRules instanceof ConditionalRules) { + return [$attribute => $attributeRules->passes() ? $attributeRules->rules() : null]; } return [$attribute => collect($attributeRules)->map(function ($rule) { From 550fc3167c908fe46dadc9acdffba0e3967a5d43 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 12 Aug 2021 12:37:19 -0500 Subject: [PATCH 3/5] allow closures on conditional --- src/Illuminate/Validation/ConditionalRules.php | 11 +++++++---- src/Illuminate/Validation/ValidationRuleParser.php | 11 ++++++----- src/Illuminate/Validation/Validator.php | 2 +- tests/Validation/ValidationRuleParserTest.php | 2 ++ 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/Illuminate/Validation/ConditionalRules.php b/src/Illuminate/Validation/ConditionalRules.php index bc9a0739ab41..f8352360b33a 100644 --- a/src/Illuminate/Validation/ConditionalRules.php +++ b/src/Illuminate/Validation/ConditionalRules.php @@ -7,7 +7,7 @@ class ConditionalRules /** * The boolean condition indicating if the rules should be added to the attribute. * - * @var bool + * @var callable|bool */ protected $condition; @@ -21,7 +21,7 @@ class ConditionalRules /** * Create a new conditional rules instance. * - * @param bool $condition + * @param callable|bool $condition * @param array|string $rules * @return void */ @@ -34,11 +34,14 @@ public function __construct($condition, $rules) /** * Determine if the conditional rules should be added. * + * @param array $data * @return bool */ - public function passes() + public function passes(array $data = []) { - return $this->condition; + return is_callable($this->condition) + ? call_user_func($this->condition, $data) + : $this->condition; } /** diff --git a/src/Illuminate/Validation/ValidationRuleParser.php b/src/Illuminate/Validation/ValidationRuleParser.php index 4a020956b2cd..293849d220af 100644 --- a/src/Illuminate/Validation/ValidationRuleParser.php +++ b/src/Illuminate/Validation/ValidationRuleParser.php @@ -279,26 +279,27 @@ protected static function normalizeRule($rule) * Expand and conditional rules in the given array of rules. * * @param array $rules + * @param array $data * @return array */ - public static function filterConditionalRules($rules) + public static function filterConditionalRules($rules, array $data = []) { - return collect($rules)->mapWithKeys(function ($attributeRules, $attribute) { + return collect($rules)->mapWithKeys(function ($attributeRules, $attribute) use ($data) { if (! is_array($attributeRules) && ! $attributeRules instanceof ConditionalRules) { return [$attribute => $attributeRules]; } if ($attributeRules instanceof ConditionalRules) { - return [$attribute => $attributeRules->passes() ? $attributeRules->rules() : null]; + return [$attribute => $attributeRules->passes($data) ? $attributeRules->rules() : null]; } - return [$attribute => collect($attributeRules)->map(function ($rule) { + return [$attribute => collect($attributeRules)->map(function ($rule) use ($data) { if (! $rule instanceof ConditionalRules) { return [$rule]; } - return $rule->passes() ? $rule->rules() : null; + return $rule->passes($data) ? $rule->rules() : null; })->filter()->flatten(1)->values()->all()]; })->filter()->all(); } diff --git a/src/Illuminate/Validation/Validator.php b/src/Illuminate/Validation/Validator.php index 3e4a6b975a06..751b7a04b7da 100755 --- a/src/Illuminate/Validation/Validator.php +++ b/src/Illuminate/Validation/Validator.php @@ -1082,7 +1082,7 @@ public function addRules($rules) // of the explicit rules needed for the given data. For example the rule // names.* would get expanded to names.0, names.1, etc. for this data. $response = (new ValidationRuleParser($this->data)) - ->explode(ValidationRuleParser::filterConditionalRules($rules)); + ->explode(ValidationRuleParser::filterConditionalRules($rules, $this->data)); $this->rules = array_merge_recursive( $this->rules, $response->rules diff --git a/tests/Validation/ValidationRuleParserTest.php b/tests/Validation/ValidationRuleParserTest.php index a3555578e2cf..4a3de60944c3 100644 --- a/tests/Validation/ValidationRuleParserTest.php +++ b/tests/Validation/ValidationRuleParserTest.php @@ -16,6 +16,7 @@ public function test_conditional_rules_are_properly_expanded_and_filtered() 'password' => Rule::when(true, 'required|min:2'), 'username' => ['required', Rule::when(true, ['min:2'])], 'address' => ['required', Rule::when(false, ['min:2'])], + 'city' => ['required', Rule::when(function (array $input) { return true; }, ['min:2'])], ]); $this->assertEquals([ @@ -23,6 +24,7 @@ public function test_conditional_rules_are_properly_expanded_and_filtered() 'password' => ['required', 'min:2'], 'username' => ['required', 'min:2'], 'address' => ['required'], + 'city' => ['required', 'min:2'], ], $rules); } } From ca854af838f59c5d0698be11b26dd4b4cbabe3e6 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 12 Aug 2021 17:37:51 +0000 Subject: [PATCH 4/5] Apply fixes from StyleCI --- tests/Validation/ValidationRuleParserTest.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/Validation/ValidationRuleParserTest.php b/tests/Validation/ValidationRuleParserTest.php index 4a3de60944c3..dab4ec5c342f 100644 --- a/tests/Validation/ValidationRuleParserTest.php +++ b/tests/Validation/ValidationRuleParserTest.php @@ -16,7 +16,9 @@ public function test_conditional_rules_are_properly_expanded_and_filtered() 'password' => Rule::when(true, 'required|min:2'), 'username' => ['required', Rule::when(true, ['min:2'])], 'address' => ['required', Rule::when(false, ['min:2'])], - 'city' => ['required', Rule::when(function (array $input) { return true; }, ['min:2'])], + 'city' => ['required', Rule::when(function (array $input) { + return true; + }, ['min:2'])], ]); $this->assertEquals([ From 578a76c2d53e416ce7037bc01c96be7877ed8442 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 12 Aug 2021 12:39:53 -0500 Subject: [PATCH 5/5] fix type hint --- src/Illuminate/Validation/Rule.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Validation/Rule.php b/src/Illuminate/Validation/Rule.php index f38c09b1524f..549a8c6d3bbe 100644 --- a/src/Illuminate/Validation/Rule.php +++ b/src/Illuminate/Validation/Rule.php @@ -18,7 +18,7 @@ class Rule /** * Create a new conditional rule set. * - * @param bool $condition + * @param callable|bool $condition * @param array|string $rules * @return \Illuminate\Validation\ConditionalRules */