diff --git a/src/Illuminate/Validation/ConditionalRules.php b/src/Illuminate/Validation/ConditionalRules.php new file mode 100644 index 000000000000..f8352360b33a --- /dev/null +++ b/src/Illuminate/Validation/ConditionalRules.php @@ -0,0 +1,56 @@ +condition = $condition; + $this->rules = $rules; + } + + /** + * Determine if the conditional rules should be added. + * + * @param array $data + * @return bool + */ + public function passes(array $data = []) + { + return is_callable($this->condition) + ? call_user_func($this->condition, $data) + : $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..549a8c6d3bbe 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 callable|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..293849d220af 100644 --- a/src/Illuminate/Validation/ValidationRuleParser.php +++ b/src/Illuminate/Validation/ValidationRuleParser.php @@ -274,4 +274,33 @@ protected static function normalizeRule($rule) return $rule; } } + + /** + * Expand and conditional rules in the given array of rules. + * + * @param array $rules + * @param array $data + * @return array + */ + public static function filterConditionalRules($rules, array $data = []) + { + 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($data) ? $attributeRules->rules() : null]; + } + + return [$attribute => collect($attributeRules)->map(function ($rule) use ($data) { + if (! $rule instanceof ConditionalRules) { + return [$rule]; + } + + 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 6fe9a609b87f..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($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 new file mode 100644 index 000000000000..dab4ec5c342f --- /dev/null +++ b/tests/Validation/ValidationRuleParserTest.php @@ -0,0 +1,32 @@ + 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'])], + 'city' => ['required', Rule::when(function (array $input) { + return true; + }, ['min:2'])], + ]); + + $this->assertEquals([ + 'name' => ['required', 'min:2'], + 'password' => ['required', 'min:2'], + 'username' => ['required', 'min:2'], + 'address' => ['required'], + 'city' => ['required', 'min:2'], + ], $rules); + } +}