Skip to content

Commit fc0a4a7

Browse files
authored
[8.x] Conditional rules (#38361)
* conditional rules * simplify some code * allow closures on conditional * Apply fixes from StyleCI * fix type hint Co-authored-by: Taylor Otwell <[email protected]>
1 parent a0021ab commit fc0a4a7

File tree

5 files changed

+130
-1
lines changed

5 files changed

+130
-1
lines changed
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<?php
2+
3+
namespace Illuminate\Validation;
4+
5+
class ConditionalRules
6+
{
7+
/**
8+
* The boolean condition indicating if the rules should be added to the attribute.
9+
*
10+
* @var callable|bool
11+
*/
12+
protected $condition;
13+
14+
/**
15+
* The rules to be added to the attribute.
16+
*
17+
* @var array
18+
*/
19+
protected $rules;
20+
21+
/**
22+
* Create a new conditional rules instance.
23+
*
24+
* @param callable|bool $condition
25+
* @param array|string $rules
26+
* @return void
27+
*/
28+
public function __construct($condition, $rules)
29+
{
30+
$this->condition = $condition;
31+
$this->rules = $rules;
32+
}
33+
34+
/**
35+
* Determine if the conditional rules should be added.
36+
*
37+
* @param array $data
38+
* @return bool
39+
*/
40+
public function passes(array $data = [])
41+
{
42+
return is_callable($this->condition)
43+
? call_user_func($this->condition, $data)
44+
: $this->condition;
45+
}
46+
47+
/**
48+
* Get the rules.
49+
*
50+
* @return array
51+
*/
52+
public function rules()
53+
{
54+
return is_string($this->rules) ? explode('|', $this->rules) : $this->rules;
55+
}
56+
}

src/Illuminate/Validation/Rule.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,18 @@ class Rule
1515
{
1616
use Macroable;
1717

18+
/**
19+
* Create a new conditional rule set.
20+
*
21+
* @param callable|bool $condition
22+
* @param array|string $rules
23+
* @return \Illuminate\Validation\ConditionalRules
24+
*/
25+
public static function when($condition, $rules)
26+
{
27+
return new ConditionalRules($condition, $rules);
28+
}
29+
1830
/**
1931
* Get a dimensions constraint builder instance.
2032
*

src/Illuminate/Validation/ValidationRuleParser.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,4 +274,33 @@ protected static function normalizeRule($rule)
274274
return $rule;
275275
}
276276
}
277+
278+
/**
279+
* Expand and conditional rules in the given array of rules.
280+
*
281+
* @param array $rules
282+
* @param array $data
283+
* @return array
284+
*/
285+
public static function filterConditionalRules($rules, array $data = [])
286+
{
287+
return collect($rules)->mapWithKeys(function ($attributeRules, $attribute) use ($data) {
288+
if (! is_array($attributeRules) &&
289+
! $attributeRules instanceof ConditionalRules) {
290+
return [$attribute => $attributeRules];
291+
}
292+
293+
if ($attributeRules instanceof ConditionalRules) {
294+
return [$attribute => $attributeRules->passes($data) ? $attributeRules->rules() : null];
295+
}
296+
297+
return [$attribute => collect($attributeRules)->map(function ($rule) use ($data) {
298+
if (! $rule instanceof ConditionalRules) {
299+
return [$rule];
300+
}
301+
302+
return $rule->passes($data) ? $rule->rules() : null;
303+
})->filter()->flatten(1)->values()->all()];
304+
})->filter()->all();
305+
}
277306
}

src/Illuminate/Validation/Validator.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1082,7 +1082,7 @@ public function addRules($rules)
10821082
// of the explicit rules needed for the given data. For example the rule
10831083
// names.* would get expanded to names.0, names.1, etc. for this data.
10841084
$response = (new ValidationRuleParser($this->data))
1085-
->explode($rules);
1085+
->explode(ValidationRuleParser::filterConditionalRules($rules, $this->data));
10861086

10871087
$this->rules = array_merge_recursive(
10881088
$this->rules, $response->rules
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
namespace Illuminate\Tests\Validation;
4+
5+
use Illuminate\Validation\Rule;
6+
use Illuminate\Validation\ValidationRuleParser;
7+
use PHPUnit\Framework\TestCase;
8+
9+
class ValidationRuleParserTest extends TestCase
10+
{
11+
public function test_conditional_rules_are_properly_expanded_and_filtered()
12+
{
13+
$rules = ValidationRuleParser::filterConditionalRules([
14+
'name' => Rule::when(true, ['required', 'min:2']),
15+
'email' => Rule::when(false, ['required', 'min:2']),
16+
'password' => Rule::when(true, 'required|min:2'),
17+
'username' => ['required', Rule::when(true, ['min:2'])],
18+
'address' => ['required', Rule::when(false, ['min:2'])],
19+
'city' => ['required', Rule::when(function (array $input) {
20+
return true;
21+
}, ['min:2'])],
22+
]);
23+
24+
$this->assertEquals([
25+
'name' => ['required', 'min:2'],
26+
'password' => ['required', 'min:2'],
27+
'username' => ['required', 'min:2'],
28+
'address' => ['required'],
29+
'city' => ['required', 'min:2'],
30+
], $rules);
31+
}
32+
}

0 commit comments

Comments
 (0)