diff --git a/src/Http/Validation/Validator.php b/src/Http/Validation/Validator.php index 0bd846bd..976f5682 100644 --- a/src/Http/Validation/Validator.php +++ b/src/Http/Validation/Validator.php @@ -22,6 +22,9 @@ class Validator 'required' => 'NotEmpty', ]; + /** @var array */ + protected $nestedRules = ['optional', 'not']; + /** * @param array $data * @param array $rules @@ -37,20 +40,38 @@ class Validator $v->with('\\Engelsystem\\Http\\Validation\\Rules', true); $value = isset($data[$key]) ? $data[$key] : null; + $values = explode('|', $values); + + $packing = []; + foreach ($this->nestedRules as $rule) { + if (in_array($rule, $values)) { + $packing[] = $rule; + } + } - foreach (explode('|', $values) as $parameters) { + $values = array_diff($values, $this->nestedRules); + foreach ($values as $parameters) { $parameters = explode(':', $parameters); $rule = array_shift($parameters); $rule = Str::camel($rule); $rule = $this->map($rule); + // To allow rules nesting + $w = $v; try { - call_user_func_array([$v, $rule], $parameters); + foreach (array_reverse(array_merge($packing, [$rule])) as $rule) { + if (!in_array($rule, $this->nestedRules)) { + call_user_func_array([$w, $rule], $parameters); + continue; + } + + $w = call_user_func_array([new RespectValidator(), $rule], [$w]); + } } catch (ComponentException $e) { throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e); } - if ($v->validate($value)) { + if ($w->validate($value)) { $this->data[$key] = $value; } else { $this->errors[$key][] = implode('.', ['validation', $key, $this->mapBack($rule)]); diff --git a/tests/Unit/Http/Validation/ValidatorTest.php b/tests/Unit/Http/Validation/ValidatorTest.php index 0790b7a8..450e5d4e 100644 --- a/tests/Unit/Http/Validation/ValidatorTest.php +++ b/tests/Unit/Http/Validation/ValidatorTest.php @@ -16,6 +16,7 @@ class ValidatorTest extends TestCase public function testValidate() { $val = new Validator(); + $this->assertTrue($val->validate( ['foo' => 'bar', 'lorem' => 'on', 'dolor' => 'bla'], ['lorem' => 'accepted'] @@ -32,12 +33,39 @@ class ValidatorTest extends TestCase ); } + /** + * @covers \Engelsystem\Http\Validation\Validator::validate + */ + public function testValidateChaining() + { + $val = new Validator(); + + $this->assertTrue($val->validate( + ['lorem' => 10], + ['lorem' => 'required|min:3|max:10'] + )); + $this->assertTrue($val->validate( + ['lorem' => 3], + ['lorem' => 'required|min:3|max:10'] + )); + + $this->assertFalse($val->validate( + ['lorem' => 2], + ['lorem' => 'required|min:3|max:10'] + )); + $this->assertFalse($val->validate( + ['lorem' => 42], + ['lorem' => 'required|min:3|max:10'] + )); + } + /** * @covers \Engelsystem\Http\Validation\Validator::validate */ public function testValidateNotImplemented() { $val = new Validator(); + $this->expectException(InvalidArgumentException::class); $val->validate( @@ -53,6 +81,7 @@ class ValidatorTest extends TestCase public function testValidateMapping() { $val = new Validator(); + $this->assertTrue($val->validate( ['foo' => 'bar'], ['foo' => 'required'] @@ -75,4 +104,39 @@ class ValidatorTest extends TestCase $val->getErrors() ); } + + /** + * @covers \Engelsystem\Http\Validation\Validator::validate + */ + public function testValidateNesting() + { + $val = new Validator(); + + $this->assertTrue($val->validate( + [], + ['foo' => 'not|required'] + )); + + $this->assertTrue($val->validate( + ['foo' => 'foo'], + ['foo' => 'not|int'] + )); + $this->assertFalse($val->validate( + ['foo' => 1], + ['foo' => 'not|int'] + )); + + $this->assertTrue($val->validate( + [], + ['foo' => 'optional|int'] + )); + $this->assertTrue($val->validate( + ['foo' => '33'], + ['foo' => 'optional|int'] + )); + $this->assertFalse($val->validate( + ['foo' => 'T'], + ['foo' => 'optional|int'] + )); + } }