diff --git a/src/ArrayInput.php b/src/ArrayInput.php index 2ee27809..0451a600 100644 --- a/src/ArrayInput.php +++ b/src/ArrayInput.php @@ -63,6 +63,7 @@ public function isValid($context = null) $hasValue = $this->hasValue(); $required = $this->isRequired(); $hasFallback = $this->hasFallback(); + $values = $this->getValue(); if (! $hasValue && $hasFallback) { $this->setValue($this->getFallbackValue()); @@ -70,17 +71,14 @@ public function isValid($context = null) } if (! $hasValue && $required) { - if ($this->errorMessage === null) { - $this->setErrorMessage('Value is required'); - } - return false; + $this->injectRequiredValidator(); + $values = [$this]; } if (!$this->continueIfEmpty() && !$this->allowEmpty()) { $this->injectNotEmptyValidator(); } $validator = $this->getValidatorChain(); - $values = $this->getValue(); $result = true; foreach ($values as $value) { $empty = ($value === null || $value === '' || $value === []); diff --git a/src/FileInput.php b/src/FileInput.php index 48aa010e..125115d5 100644 --- a/src/FileInput.php +++ b/src/FileInput.php @@ -118,16 +118,15 @@ public function isValid($context = null) $required = $this->isRequired(); $allowEmpty = $this->allowEmpty(); $continueIfEmpty = $this->continueIfEmpty(); + $validator = $this->getValidatorChain(); if (! $hasValue && ! $required) { return true; } if (! $hasValue && $required && ! $this->hasFallback()) { - if ($this->errorMessage === null) { - $this->setErrorMessage('Value is required'); - } - return false; + $this->injectRequiredValidator(); + return $validator->isValid($this); } if ($empty && ! $required && ! $continueIfEmpty) { @@ -139,7 +138,6 @@ public function isValid($context = null) } $this->injectUploadValidator(); - $validator = $this->getValidatorChain(); //$value = $this->getValue(); // Do not run the filters yet for File uploads (see getValue()) if (!is_array($rawValue)) { diff --git a/src/Input.php b/src/Input.php index 50c98fc6..e182ea82 100644 --- a/src/Input.php +++ b/src/Input.php @@ -59,6 +59,11 @@ class Input implements */ protected $notEmptyValidator = false; + /** + * @var bool If required validator is in the validator chain. + */ + protected $requiredValidator = false; + /** * @var bool */ @@ -403,10 +408,8 @@ public function isValid($context = null) } if (! $hasValue && $required) { - if ($this->errorMessage === null) { - $this->setErrorMessage('Value is required'); - } - return false; + $this->injectRequiredValidator(); + $value = $this; } if ($empty && ! $required && ! $continueIfEmpty) { @@ -483,4 +486,25 @@ protected function injectNotEmptyValidator() $chain->prependValidator(new NotEmpty(), true); } + + /** + * @return void + */ + protected function injectRequiredValidator() + { + $chain = $this->getValidatorChain(); + + // Check if Required validator is already in chain + $validators = $chain->getValidators(); + foreach ($validators as $validator) { + if ($validator['instance'] instanceof Validator\Required) { + $this->requiredValidator = true; + return; + } + } + + $this->requiredValidator = true; + + $chain->prependValidator(new Validator\Required(), true); + } } diff --git a/src/Validator/Required.php b/src/Validator/Required.php new file mode 100644 index 00000000..48af1cf7 --- /dev/null +++ b/src/Validator/Required.php @@ -0,0 +1,41 @@ + 'Invalid type given. Zend\InputFilter\Input is required', + self::REQUIRED => 'Value is required', + ]; + + public function isValid($value) + { + if (!($value instanceof Input)) { + $this->error(self::INVALID); + return false; + } + + $input = $value; + + if ($input->hasValue()) { // If has value then all is ok + return true; + } + + if ($input->isRequired()) { // It's Required and value was not set. + $this->error(self::REQUIRED); + return false; + } + + return true; + } +} diff --git a/test/InputTest.php b/test/InputTest.php index bd88157c..dd10e879 100644 --- a/test/InputTest.php +++ b/test/InputTest.php @@ -168,11 +168,15 @@ public function testRequiredWithoutFallbackAndValueNotSetThenFail() $input = $this->input; $input->setRequired(true); + $expectedMessages = [ + 'inputRequired' => 'Value is required', + ]; + $this->assertFalse( $input->isValid(), 'isValid() should be return always false when no fallback value, is required, and not data is set.' ); - $this->assertEquals(['Value is required'], $input->getMessages(), 'getMessages() value not match'); + $this->assertEquals($expectedMessages, $input->getMessages(), 'getMessages() value not match'); } public function testRequiredWithoutFallbackAndValueNotSetThenFailWithCustomErrorMessage() diff --git a/test/Validator/RequiredTest.php b/test/Validator/RequiredTest.php new file mode 100644 index 00000000..52d031fe --- /dev/null +++ b/test/Validator/RequiredTest.php @@ -0,0 +1,78 @@ +validator = new Required(); + } + + /** + * @dataProvider inputProvider + */ + public function testValid($input, $expectedIsValid, $expectedMessages) + { + $this->assertEquals( + $expectedIsValid, + $this->validator->isValid($input), + 'isValid() value not match. Detail: ' . json_encode($this->validator->getMessages()) + ); + + $this->assertEquals( + $expectedMessages, + $this->validator->getMessages(), + 'getMessages() value not match.' + ); + } + + public function inputProvider() + { + $requiredMsg = [ + Required::REQUIRED => 'Value is required', + ]; + + // @codingStandardsIgnoreStart + return [ + // Description => [$input, isValid, getMessages] + 'Required: T. Value: Set' => [$this->createInputMock(true, true) , true , []], + 'Required: T. Value: Not set' => [$this->createInputMock(true, false) , false, $requiredMsg], + 'Required: F. Value: set' => [$this->createInputMock(false, true) , true , []], + 'Required: F. Value: Not set' => [$this->createInputMock(false, false), true , []], + ]; + // @codingStandardsIgnoreEnd + } + + /** + * @param bool $required + * @param bool $hasValue + * + * @return Input|MockObject + */ + protected function createInputMock($required, $hasValue) + { + /** @var Input|MockObject $input */ + $input = $this->getMock(Input::class); + + $input->method('isRequired') + ->willReturn($required) + ; + + $input->method('hasValue') + ->willReturn($hasValue) + ; + + return $input; + } +}