From c77730699e2ff59f8204556e5e2178c66412b541 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Bogusz?= Date: Thu, 2 Apr 2020 22:03:50 +0200 Subject: [PATCH] [Validator] Add invalid datetime message in Range validator --- .../Component/Validator/Constraints/Range.php | 1 + .../Validator/Constraints/RangeValidator.php | 40 ++++++-- .../Tests/Constraints/RangeValidatorTest.php | 97 ++++++++++++++++++- 3 files changed, 127 insertions(+), 11 deletions(-) diff --git a/src/Symfony/Component/Validator/Constraints/Range.php b/src/Symfony/Component/Validator/Constraints/Range.php index 3ccbf89b628a7..7bbe69fcc3193 100644 --- a/src/Symfony/Component/Validator/Constraints/Range.php +++ b/src/Symfony/Component/Validator/Constraints/Range.php @@ -41,6 +41,7 @@ class Range extends Constraint public $minMessage = 'This value should be {{ limit }} or more.'; public $maxMessage = 'This value should be {{ limit }} or less.'; public $invalidMessage = 'This value should be a valid number.'; + public $invalidDateTimeMessage = 'This value should be a valid datetime.'; public $min; public $minPropertyPath; public $max; diff --git a/src/Symfony/Component/Validator/Constraints/RangeValidator.php b/src/Symfony/Component/Validator/Constraints/RangeValidator.php index 2aa81666f793c..7a9ad79e04ad6 100644 --- a/src/Symfony/Component/Validator/Constraints/RangeValidator.php +++ b/src/Symfony/Component/Validator/Constraints/RangeValidator.php @@ -44,18 +44,25 @@ public function validate($value, Constraint $constraint) return; } + $min = $this->getLimit($constraint->minPropertyPath, $constraint->min, $constraint); + $max = $this->getLimit($constraint->maxPropertyPath, $constraint->max, $constraint); + if (!is_numeric($value) && !$value instanceof \DateTimeInterface) { - $this->context->buildViolation($constraint->invalidMessage) - ->setParameter('{{ value }}', $this->formatValue($value, self::PRETTY_DATE)) - ->setCode(Range::INVALID_CHARACTERS_ERROR) - ->addViolation(); + if ($this->isParsableDatetimeString($min) && $this->isParsableDatetimeString($max)) { + $this->context->buildViolation($constraint->invalidDateTimeMessage) + ->setParameter('{{ value }}', $this->formatValue($value, self::PRETTY_DATE)) + ->setCode(Range::INVALID_CHARACTERS_ERROR) + ->addViolation(); + } else { + $this->context->buildViolation($constraint->invalidMessage) + ->setParameter('{{ value }}', $this->formatValue($value, self::PRETTY_DATE)) + ->setCode(Range::INVALID_CHARACTERS_ERROR) + ->addViolation(); + } return; } - $min = $this->getLimit($constraint->minPropertyPath, $constraint->min, $constraint); - $max = $this->getLimit($constraint->maxPropertyPath, $constraint->max, $constraint); - // Convert strings to DateTimes if comparing another DateTime // This allows to compare with any date/time value supported by // the DateTime constructor: @@ -182,4 +189,23 @@ private function getPropertyAccessor(): PropertyAccessorInterface return $this->propertyAccessor; } + + private function isParsableDatetimeString($boundary): bool + { + if (null === $boundary) { + return true; + } + + if (!\is_string($boundary)) { + return false; + } + + try { + new \DateTime($boundary); + } catch (\Exception $e) { + return false; + } + + return true; + } } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/RangeValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/RangeValidatorTest.php index ff1497c9238d8..3d7a773a21486 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/RangeValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/RangeValidatorTest.php @@ -379,13 +379,102 @@ public function getInvalidValues() public function testNonNumeric() { - $this->validator->validate('abcd', new Range([ + $constraint = new Range([ 'min' => 10, 'max' => 20, - 'invalidMessage' => 'myMessage', - ])); + ]); - $this->buildViolation('myMessage') + $this->validator->validate('abcd', $constraint); + + $this->buildViolation($constraint->invalidMessage) + ->setParameter('{{ value }}', '"abcd"') + ->setCode(Range::INVALID_CHARACTERS_ERROR) + ->assertRaised(); + } + + public function testNonNumericWithParsableDatetimeMinAndMaxNull() + { + $constraint = new Range([ + 'min' => 'March 10, 2014', + ]); + + $this->validator->validate('abcd', $constraint); + + $this->buildViolation($constraint->invalidDateTimeMessage) + ->setParameter('{{ value }}', '"abcd"') + ->setCode(Range::INVALID_CHARACTERS_ERROR) + ->assertRaised(); + } + + public function testNonNumericWithParsableDatetimeMaxAndMinNull() + { + $constraint = new Range([ + 'max' => 'March 20, 2014', + ]); + + $this->validator->validate('abcd', $constraint); + + $this->buildViolation($constraint->invalidDateTimeMessage) + ->setParameter('{{ value }}', '"abcd"') + ->setCode(Range::INVALID_CHARACTERS_ERROR) + ->assertRaised(); + } + + public function testNonNumericWithParsableDatetimeMinAndMax() + { + $constraint = new Range([ + 'min' => 'March 10, 2014', + 'max' => 'March 20, 2014', + ]); + + $this->validator->validate('abcd', $constraint); + + $this->buildViolation($constraint->invalidDateTimeMessage) + ->setParameter('{{ value }}', '"abcd"') + ->setCode(Range::INVALID_CHARACTERS_ERROR) + ->assertRaised(); + } + + public function testNonNumericWithNonParsableDatetimeMin() + { + $constraint = new Range([ + 'min' => 'March 40, 2014', + 'max' => 'March 20, 2014', + ]); + + $this->validator->validate('abcd', $constraint); + + $this->buildViolation($constraint->invalidMessage) + ->setParameter('{{ value }}', '"abcd"') + ->setCode(Range::INVALID_CHARACTERS_ERROR) + ->assertRaised(); + } + + public function testNonNumericWithNonParsableDatetimeMax() + { + $constraint = new Range([ + 'min' => 'March 10, 2014', + 'max' => 'March 50, 2014', + ]); + + $this->validator->validate('abcd', $constraint); + + $this->buildViolation($constraint->invalidMessage) + ->setParameter('{{ value }}', '"abcd"') + ->setCode(Range::INVALID_CHARACTERS_ERROR) + ->assertRaised(); + } + + public function testNonNumericWithNonParsableDatetimeMinAndMax() + { + $constraint = new Range([ + 'min' => 'March 40, 2014', + 'max' => 'March 50, 2014', + ]); + + $this->validator->validate('abcd', $constraint); + + $this->buildViolation($constraint->invalidMessage) ->setParameter('{{ value }}', '"abcd"') ->setCode(Range::INVALID_CHARACTERS_ERROR) ->assertRaised();