Skip to content
Merged
25 changes: 17 additions & 8 deletions src/Illuminate/Validation/ValidationRuleParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,12 @@ protected function explodeRules($rules)
protected function explodeExplicitRule($rule, $attribute)
{
if (is_string($rule)) {
return explode('|', $rule);
} elseif (is_object($rule)) {
[$name] = static::parseStringRule($rule);

return static::ruleIsRegex($name) ? [$rule] : explode('|', $rule);
}

if (is_object($rule)) {
return Arr::wrap($this->prepareRule($rule, $attribute));
}

Expand Down Expand Up @@ -272,13 +276,18 @@ protected static function parseStringRule($rule)
*/
protected static function parseParameters($rule, $parameter)
{
$rule = strtolower($rule);

if (in_array($rule, ['regex', 'not_regex', 'notregex'], true)) {
return [$parameter];
}
return static::ruleIsRegex($rule) ? [$parameter] : str_getcsv($parameter);
}

return str_getcsv($parameter);
/**
* Determine if the rule is a regular expression.
*
* @param string $rule
* @return bool
*/
protected static function ruleIsRegex($rule)
{
return in_array(strtolower($rule), ['regex', 'not_regex', 'notregex'], true);
}

/**
Expand Down
56 changes: 56 additions & 0 deletions tests/Validation/ValidationForEachTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,62 @@ public function testForEachCallbacksCanReturnDifferentRules()
], $v->getMessageBag()->toArray());
}

public function testForEachCallbacksDoNotBreakRegexRules()
{
$data = [
'items' => [
['users' => [['type' => 'super'], ['type' => 'invalid']]],
],
];

$rules = [
'items.*' => Rule::forEach(function () {
return ['users.*.type' => 'regex:/^(super|admin)$/i'];
}),
];

$trans = $this->getIlluminateArrayTranslator();

$v = new Validator($trans, $data, $rules);

$this->assertFalse($v->passes());

$this->assertEquals([
'items.0.users.1.type' => ['validation.regex'],
], $v->getMessageBag()->toArray());
}

public function testForEachCallbacksCanContainMultipleRegexRules()
{
$data = [
'items' => [
['users' => [['type' => 'super'], ['type' => 'invalid']]],
],
];

$rules = [
'items.*' => Rule::forEach(function () {
return ['users.*.type' => [
'regex:/^(super)$/i',
'notregex:/^(invalid)$/i',
]];
}),
];

$trans = $this->getIlluminateArrayTranslator();

$v = new Validator($trans, $data, $rules);

$this->assertFalse($v->passes());

$this->assertEquals([
'items.0.users.1.type' => [
'validation.regex',
'validation.notregex',
],
], $v->getMessageBag()->toArray());
}

protected function getTranslator()
{
return m::mock(TranslatorContract::class);
Expand Down
60 changes: 60 additions & 0 deletions tests/Validation/ValidationRuleParserTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,66 @@ public function testEmptyConditionalRulesArePreserved()
], $rules);
}

public function testExplodeProperlyParsesSingleRegexRule()
{
$data = ['items' => [['type' => 'foo']]];

$exploded = (new ValidationRuleParser($data))->explode(
['items.*.type' => 'regex:/^(foo|bar)$/i']
);

$this->assertEquals('regex:/^(foo|bar)$/i', $exploded->rules['items.0.type'][0]);
}

public function testExplodeProperlyParsesRegexWithArrayOfRules()
{
$data = ['items' => [['type' => 'foo']]];

$exploded = (new ValidationRuleParser($data))->explode(
['items.*.type' => ['in:foo', 'regex:/^(foo|bar)$/i']]
);

$this->assertEquals('in:foo', $exploded->rules['items.0.type'][0]);
$this->assertEquals('regex:/^(foo|bar)$/i', $exploded->rules['items.0.type'][1]);
}

public function testExplodeProperlyParsesRegexThatDoesNotContainPipe()
{
$data = ['items' => [['type' => 'foo']]];

$exploded = (new ValidationRuleParser($data))->explode(
['items.*.type' => 'in:foo|regex:/^(bar)$/i']
);

$this->assertEquals('in:foo', $exploded->rules['items.0.type'][0]);
$this->assertEquals('regex:/^(bar)$/i', $exploded->rules['items.0.type'][1]);
}

public function testExplodeFailsParsingRegexWithOtherRulesInSingleString()
{
$data = ['items' => [['type' => 'foo']]];

$exploded = (new ValidationRuleParser($data))->explode(
['items.*.type' => 'in:foo|regex:/^(foo|bar)$/i']
);

$this->assertEquals('in:foo', $exploded->rules['items.0.type'][0]);
$this->assertEquals('regex:/^(foo', $exploded->rules['items.0.type'][1]);
$this->assertEquals('bar)$/i', $exploded->rules['items.0.type'][2]);
}

public function testExplodeProperlyFlattensRuleArraysOfArrays()
{
$data = ['items' => [['type' => 'foo']]];

$exploded = (new ValidationRuleParser($data))->explode(
['items.*.type' => ['in:foo', [[['regex:/^(foo|bar)$/i']]]]]
);

$this->assertEquals('in:foo', $exploded->rules['items.0.type'][0]);
$this->assertEquals('regex:/^(foo|bar)$/i', $exploded->rules['items.0.type'][1]);
}

public function testExplodeGeneratesNestedRules()
{
$parser = (new ValidationRuleParser([
Expand Down
3 changes: 3 additions & 0 deletions tests/Validation/ValidationValidatorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3820,6 +3820,9 @@ public function testValidateRegex()

$v = new Validator($trans, ['x' => 12], ['x' => 'Regex:/^12$/i']);
$this->assertTrue($v->passes());

$v = new Validator($trans, ['x' => ['y' => ['z' => 'james']]], ['x.*.z' => ['Regex:/^(taylor|james)$/i']]);
$this->assertTrue($v->passes());
}

public function testValidateNotRegex()
Expand Down