Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitf4b9bf6

Browse files
committed
[Validator] Support \DateInterval in comparison constraints
1 parent9eb7cb1 commitf4b9bf6

File tree

20 files changed

+549
-19
lines changed

20 files changed

+549
-19
lines changed

‎src/Symfony/Component/Validator/CHANGELOG.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ CHANGELOG
88
* added option`alpha3` to`Country` constraint
99
* allow to define a reusable set of constraints by extending the`Compound` constraint
1010
* added`Sequentially` constraint, to sequentially validate a set of constraints (any violation raised will prevent further validation of the nested constraints)
11+
* added support to validate`\DateInterval` instances with relative string date formats in the`Range` and all comparisons constraints
1112

1213
5.0.0
1314
-----

‎src/Symfony/Component/Validator/ConstraintValidator.php‎

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ abstract class ConstraintValidator implements ConstraintValidatorInterface
3131
*/
3232
constOBJECT_TO_STRING =2;
3333

34+
/**
35+
* Whether to format {@link \DateInterval} objects as human readable strings
36+
* eg: 6 hours, 1 minute and 2 seconds.
37+
*/
38+
constPRETTY_DATE_INTERVAL =4;
39+
3440
/**
3541
* @var ExecutionContextInterface
3642
*/
@@ -98,6 +104,37 @@ protected function formatValue($value, int $format = 0)
98104
return$value->format('Y-m-d H:i:s');
99105
}
100106

107+
if (($format &self::PRETTY_DATE_INTERVAL) &&$valueinstanceof \DateInterval) {
108+
$formattedValueParts = [];
109+
foreach ([
110+
'y' =>'year',
111+
'm' =>'month',
112+
'd' =>'day',
113+
'h' =>'hour',
114+
'i' =>'minute',
115+
's' =>'second',
116+
'f' =>'microsecond',
117+
]as$p =>$label) {
118+
if (!$formattedValue =$value->format('%'.$p)) {
119+
continue;
120+
}
121+
122+
if ($formattedValue >1) {
123+
$label .='s';
124+
}
125+
126+
$formattedValueParts[] =$formattedValue.''.$label;
127+
}
128+
129+
if (!$formattedValueParts) {
130+
return'0';
131+
}
132+
133+
$lastFormattedValuePart =array_pop($formattedValueParts);
134+
135+
return$value->format('%r').(!$formattedValueParts ?$lastFormattedValuePart :implode(',',$formattedValueParts).' and'.$lastFormattedValuePart);
136+
}
137+
101138
if (\is_object($value)) {
102139
if (($format &self::OBJECT_TO_STRING) &&method_exists($value,'__toString')) {
103140
return$value->__toString();

‎src/Symfony/Component/Validator/Constraints/AbstractComparisonValidator.php‎

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
useSymfony\Component\Validator\ConstraintValidator;
1919
useSymfony\Component\Validator\Exception\ConstraintDefinitionException;
2020
useSymfony\Component\Validator\Exception\UnexpectedTypeException;
21+
useSymfony\Component\Validator\Util\DateIntervalComparisonHelper;
2122

2223
/**
2324
* Provides a base class for the validation of property comparisons.
@@ -61,11 +62,14 @@ public function validate($value, Constraint $constraint)
6162
$comparedValue =$constraint->value;
6263
}
6364

64-
// Convert strings to DateTimes if comparing another DateTime
65-
// This allows to compare with any date/time value supported by
66-
// the DateTime constructor:
67-
// https://php.net/datetime.formats
65+
$isDateIntervalComparison =false;
66+
6867
if (\is_string($comparedValue) &&$valueinstanceof \DateTimeInterface) {
68+
// Convert strings to DateTimes if comparing another DateTime
69+
// This allows to compare with any date/time value supported by
70+
// the DateTime constructor:
71+
// https://php.net/datetime.formats
72+
6973
// If $value is immutable, convert the compared value to a DateTimeImmutable too, otherwise use DateTime
7074
$dateTimeClass =$valueinstanceof \DateTimeImmutable ? \DateTimeImmutable::class : \DateTime::class;
7175

@@ -74,13 +78,22 @@ public function validate($value, Constraint $constraint)
7478
}catch (\Exception$e) {
7579
thrownewConstraintDefinitionException(sprintf('The compared value "%s" could not be converted to a "%s" instance in the "%s" constraint.',$comparedValue,$dateTimeClass,\get_class($constraint)));
7680
}
81+
}elseif ($isDateIntervalComparison = DateIntervalComparisonHelper::supports($value,$comparedValue)) {
82+
$originalValue =$value;
83+
$value = DateIntervalComparisonHelper::convertValue($dateIntervalReference =new \DateTimeImmutable(),$value);
84+
85+
try {
86+
$comparedValue = DateIntervalComparisonHelper::convertComparedValue($dateIntervalReference,$comparedValue);
87+
}catch (\InvalidArgumentException$e) {
88+
thrownewConstraintDefinitionException(sprintf('The compared value "%s" could not be converted to a "DateTimeImmutable" instance in the "%s" constraint.',$comparedValue,\get_class($constraint)));
89+
}
7790
}
7891

7992
if (!$this->compareValues($value,$comparedValue)) {
8093
$violationBuilder =$this->context->buildViolation($constraint->message)
81-
->setParameter('{{ value }}',$this->formatValue($value,self::OBJECT_TO_STRING |self::PRETTY_DATE))
82-
->setParameter('{{ compared_value }}',$this->formatValue($comparedValue,self::OBJECT_TO_STRING |self::PRETTY_DATE))
83-
->setParameter('{{ compared_value_type }}',$this->formatTypeOf($comparedValue))
94+
->setParameter('{{ value }}',$this->formatValue(!$isDateIntervalComparison ?$value :$originalValue,self::OBJECT_TO_STRING |self::PRETTY_DATE |self::PRETTY_DATE_INTERVAL))
95+
->setParameter('{{ compared_value }}',$this->formatValue($messageComparedValue = (!$isDateIntervalComparison ?$comparedValue :$dateIntervalReference->diff($comparedValue)),self::OBJECT_TO_STRING |self::PRETTY_DATE |self::PRETTY_DATE_INTERVAL))
96+
->setParameter('{{ compared_value_type }}',$this->formatTypeOf($messageComparedValue))
8497
->setCode($this->getErrorCode());
8598

8699
if (null !==$path) {

‎src/Symfony/Component/Validator/Constraints/RangeValidator.php‎

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
useSymfony\Component\Validator\ConstraintValidator;
1919
useSymfony\Component\Validator\Exception\ConstraintDefinitionException;
2020
useSymfony\Component\Validator\Exception\UnexpectedTypeException;
21+
useSymfony\Component\Validator\Util\DateIntervalComparisonHelper;
2122

2223
/**
2324
* @author Bernhard Schussek <bschussek@gmail.com>
@@ -44,7 +45,7 @@ public function validate($value, Constraint $constraint)
4445
return;
4546
}
4647

47-
if (!is_numeric($value) && !$valueinstanceof \DateTimeInterface) {
48+
if (!is_numeric($value) && !$valueinstanceof \DateTimeInterface && !$valueinstanceof \DateInterval) {
4849
$this->context->buildViolation($constraint->invalidMessage)
4950
->setParameter('{{ value }}',$this->formatValue($value,self::PRETTY_DATE))
5051
->setCode(Range::INVALID_CHARACTERS_ERROR)
@@ -56,11 +57,15 @@ public function validate($value, Constraint $constraint)
5657
$min =$this->getLimit($constraint->minPropertyPath,$constraint->min,$constraint);
5758
$max =$this->getLimit($constraint->maxPropertyPath,$constraint->max,$constraint);
5859

59-
// Convert strings to DateTimes if comparing another DateTime
60-
// This allows to compare with any date/time value supported by
61-
// the DateTime constructor:
62-
// https://php.net/datetime.formats
60+
$minIsDateIntervalComparison =false;
61+
$maxIsDateIntervalComparison =false;
62+
6363
if ($valueinstanceof \DateTimeInterface) {
64+
// Convert strings to DateTimes if comparing another DateTime
65+
// This allows to compare with any date/time value supported by
66+
// the DateTime constructor:
67+
// https://php.net/datetime.formats
68+
6469
$dateTimeClass =null;
6570

6671
if (\is_string($min)) {
@@ -82,16 +87,37 @@ public function validate($value, Constraint $constraint)
8287
thrownewConstraintDefinitionException(sprintf('The max value "%s" could not be converted to a "%s" instance in the "%s" constraint.',$max,$dateTimeClass,\get_class($constraint)));
8388
}
8489
}
90+
}elseif (($minIsDateIntervalComparison = DateIntervalComparisonHelper::supports($value,$min)) || ($maxIsDateIntervalComparison = DateIntervalComparisonHelper::supports($value,$max))) {
91+
$originalValue =$value;
92+
$value = DateIntervalComparisonHelper::convertValue($dateIntervalReference =new \DateTimeImmutable(),$value);
93+
94+
if ($minIsDateIntervalComparison) {
95+
try {
96+
$min = DateIntervalComparisonHelper::convertComparedValue($dateIntervalReference,$min);
97+
}catch (\InvalidArgumentException$e) {
98+
thrownewConstraintDefinitionException(sprintf('The max value "%s" could not be converted to a "DateTimeImmutable" instance in the "%s" constraint.',$max,\get_class($constraint)));
99+
}
100+
101+
$maxIsDateIntervalComparison = DateIntervalComparisonHelper::supports($originalValue,$max);
102+
}
103+
104+
if ($maxIsDateIntervalComparison) {
105+
try {
106+
$max = DateIntervalComparisonHelper::convertComparedValue($dateIntervalReference,$max);
107+
}catch (\InvalidArgumentException$e) {
108+
thrownewConstraintDefinitionException(sprintf('The min value "%s" could not be converted to a "DateTimeImmutable" instance in the "%s" constraint.',$min,\get_class($constraint)));
109+
}
110+
}
85111
}
86112

87113
$hasLowerLimit =null !==$min;
88114
$hasUpperLimit =null !==$max;
89115

90116
if ($hasLowerLimit &&$hasUpperLimit && ($value <$min ||$value >$max)) {
91117
$violationBuilder =$this->context->buildViolation($constraint->notInRangeMessage)
92-
->setParameter('{{ value }}',$this->formatValue($value,self::PRETTY_DATE))
93-
->setParameter('{{ min }}',$this->formatValue($min,self::PRETTY_DATE))
94-
->setParameter('{{ max }}',$this->formatValue($max,self::PRETTY_DATE))
118+
->setParameter('{{ value }}',$this->formatValue(!$minIsDateIntervalComparison && !$maxIsDateIntervalComparison ?$value :$originalValue,self::PRETTY_DATE |self::PRETTY_DATE_INTERVAL))
119+
->setParameter('{{ min }}',$this->formatValue(!$minIsDateIntervalComparison ?$min :$dateIntervalReference->diff($min),self::PRETTY_DATE |self::PRETTY_DATE_INTERVAL))
120+
->setParameter('{{ max }}',$this->formatValue(!$maxIsDateIntervalComparison ?$max :$dateIntervalReference->diff($max),self::PRETTY_DATE |self::PRETTY_DATE_INTERVAL))
95121
->setCode(Range::NOT_IN_RANGE_ERROR);
96122

97123
if (null !==$constraint->maxPropertyPath) {
@@ -109,8 +135,8 @@ public function validate($value, Constraint $constraint)
109135

110136
if ($hasUpperLimit &&$value >$max) {
111137
$violationBuilder =$this->context->buildViolation($constraint->maxMessage)
112-
->setParameter('{{ value }}',$this->formatValue($value,self::PRETTY_DATE))
113-
->setParameter('{{ limit }}',$this->formatValue($max,self::PRETTY_DATE))
138+
->setParameter('{{ value }}',$this->formatValue(!$minIsDateIntervalComparison && !$maxIsDateIntervalComparison ?$value :$originalValue,self::PRETTY_DATE |self::PRETTY_DATE_INTERVAL))
139+
->setParameter('{{ limit }}',$this->formatValue(!$maxIsDateIntervalComparison ?$max :$dateIntervalReference->diff($max),self::PRETTY_DATE |self::PRETTY_DATE_INTERVAL))
114140
->setCode(Range::TOO_HIGH_ERROR);
115141

116142
if (null !==$constraint->maxPropertyPath) {
@@ -128,8 +154,8 @@ public function validate($value, Constraint $constraint)
128154

129155
if ($hasLowerLimit &&$value <$min) {
130156
$violationBuilder =$this->context->buildViolation($constraint->minMessage)
131-
->setParameter('{{ value }}',$this->formatValue($value,self::PRETTY_DATE))
132-
->setParameter('{{ limit }}',$this->formatValue($min,self::PRETTY_DATE))
157+
->setParameter('{{ value }}',$this->formatValue(!$minIsDateIntervalComparison && !$maxIsDateIntervalComparison ?$value :$originalValue,self::PRETTY_DATE |self::PRETTY_DATE_INTERVAL))
158+
->setParameter('{{ limit }}',$this->formatValue(!$minIsDateIntervalComparison ?$min :$dateIntervalReference->diff($min),self::PRETTY_DATE |self::PRETTY_DATE_INTERVAL))
133159
->setCode(Range::TOO_LOW_ERROR);
134160

135161
if (null !==$constraint->maxPropertyPath) {

‎src/Symfony/Component/Validator/Tests/ConstraintValidatorTest.php‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ public function formatValueProvider()
3030
$defaultTimezone =date_default_timezone_get();
3131
date_default_timezone_set('Europe/Moscow');// GMT+3
3232

33+
$negativeDateInterval =new \DateInterval('PT30S');
34+
$negativeDateInterval->invert =1;
35+
3336
$data = [
3437
['true',true],
3538
['false',false],
@@ -44,6 +47,13 @@ public function formatValueProvider()
4447
[class_exists(\IntlDateFormatter::class) ?'Feb 2, 1971, 8:00 AM' :'1971-02-02 08:00:00',$dateTime, ConstraintValidator::PRETTY_DATE],
4548
[class_exists(\IntlDateFormatter::class) ?'Jan 1, 1970, 6:00 AM' :'1970-01-01 06:00:00',new \DateTimeImmutable('1970-01-01T06:00:00Z'), ConstraintValidator::PRETTY_DATE],
4649
[class_exists(\IntlDateFormatter::class) ?'Jan 1, 1970, 3:00 PM' :'1970-01-01 15:00:00', (new \DateTimeImmutable('1970-01-01T23:00:00'))->setTimezone(new \DateTimeZone('America/New_York')), ConstraintValidator::PRETTY_DATE],
50+
['object',new \DateInterval('PT30S')],
51+
['1 year, 1 month, 1 day, 1 hour, 1 minute and 1 second',new \DateInterval('P1Y1M1DT1H1M1S'), ConstraintValidator::PRETTY_DATE_INTERVAL],
52+
['3 months and 4 seconds',new \DateInterval('P3MT4S'), ConstraintValidator::PRETTY_DATE_INTERVAL],
53+
['0',new \DateInterval('PT0S'), ConstraintValidator::PRETTY_DATE_INTERVAL],
54+
['0', ($dateTime =new \DateTimeImmutable())->diff($dateTime), ConstraintValidator::PRETTY_DATE_INTERVAL],
55+
['7 days',new \DateInterval('P1W'), ConstraintValidator::PRETTY_DATE_INTERVAL],
56+
['-30 seconds',$negativeDateInterval, ConstraintValidator::PRETTY_DATE_INTERVAL],
4757
];
4858

4959
date_default_timezone_set($defaultTimezone);

‎src/Symfony/Component/Validator/Tests/Constraints/EqualToValidatorTest.php‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ protected function getErrorCode(): ?string
4040
*/
4141
publicfunctionprovideValidComparisons():array
4242
{
43+
$negativeDateInterval =new \DateInterval('PT1H');
44+
$negativeDateInterval->invert =1;
45+
4346
return [
4447
[3,3],
4548
[3,'3'],
@@ -49,6 +52,9 @@ public function provideValidComparisons(): array
4952
[new \DateTime('2000-01-01 UTC'),'2000-01-01 UTC'],
5053
[newComparisonTest_Class(5),newComparisonTest_Class(5)],
5154
[null,1],
55+
['1 == 1 (string)' =>new \DateInterval('PT1H'),'+1 hour'],
56+
['1 == 1 (\DateInterval instance)' =>new \DateInterval('PT1H'),new \DateInterval('PT1H')],
57+
['-1 == -1' =>$negativeDateInterval,'-1 hour'],
5258
];
5359
}
5460

@@ -67,13 +73,19 @@ public function provideValidComparisonsToPropertyPath(): array
6773
*/
6874
publicfunctionprovideInvalidComparisons():array
6975
{
76+
$negativeDateInterval =new \DateInterval('PT1H');
77+
$negativeDateInterval->invert =1;
78+
7079
return [
7180
[1,'1',2,'2','integer'],
7281
['22','"22"','333','"333"','string'],
7382
[new \DateTime('2001-01-01'),'Jan 1, 2001, 12:00 AM',new \DateTime('2000-01-01'),'Jan 1, 2000, 12:00 AM','DateTime'],
7483
[new \DateTime('2001-01-01'),'Jan 1, 2001, 12:00 AM','2000-01-01','Jan 1, 2000, 12:00 AM','DateTime'],
7584
[new \DateTime('2001-01-01 UTC'),'Jan 1, 2001, 12:00 AM','2000-01-01 UTC','Jan 1, 2000, 12:00 AM','DateTime'],
7685
[newComparisonTest_Class(4),'4',newComparisonTest_Class(5),'5',__NAMESPACE__.'\ComparisonTest_Class'],
86+
['1 != 2 (string)' =>new \DateInterval('PT1H'),'1 hour','+2 hour','2 hours', \DateInterval::class],
87+
['1 != 2 (\DateInterval instance)' =>new \DateInterval('PT1H'),'1 hour',new \DateInterval('PT2H'),'2 hours', \DateInterval::class],
88+
['-1 != -2' =>$negativeDateInterval,'-1 hour','-2 hours','-2 hours', \DateInterval::class],
7789
];
7890
}
7991

‎src/Symfony/Component/Validator/Tests/Constraints/GreaterThanOrEqualValidatorTest.php‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ protected function getErrorCode(): ?string
4040
*/
4141
publicfunctionprovideValidComparisons():array
4242
{
43+
$negativeDateInterval =new \DateInterval('PT30S');
44+
$negativeDateInterval->invert =1;
45+
4346
return [
4447
[3,2],
4548
[1,1],
@@ -52,6 +55,12 @@ public function provideValidComparisons(): array
5255
['a','a'],
5356
['z','a'],
5457
[null,1],
58+
['30 > 29 (string)' =>new \DateInterval('PT30S'),'+29 seconds'],
59+
['30 > 29 (\DateInterval instance)' =>new \DateInterval('PT30S'),new \DateInterval('PT29S')],
60+
['30 = 30 (string)' =>new \DateInterval('PT30S'),'+30 seconds'],
61+
['30 = 30 (\DateInterval instance)' =>new \DateInterval('PT30S'),new \DateInterval('PT30S')],
62+
['-30 > -31' =>$negativeDateInterval,'-31 seconds'],
63+
['-30 = -30' =>$negativeDateInterval,'-30 seconds'],
5564
];
5665
}
5766

@@ -71,12 +80,18 @@ public function provideValidComparisonsToPropertyPath(): array
7180
*/
7281
publicfunctionprovideInvalidComparisons():array
7382
{
83+
$negativeDateInterval =new \DateInterval('PT30S');
84+
$negativeDateInterval->invert =1;
85+
7486
return [
7587
[1,'1',2,'2','integer'],
7688
[new \DateTime('2000/01/01'),'Jan 1, 2000, 12:00 AM',new \DateTime('2005/01/01'),'Jan 1, 2005, 12:00 AM','DateTime'],
7789
[new \DateTime('2000/01/01'),'Jan 1, 2000, 12:00 AM','2005/01/01','Jan 1, 2005, 12:00 AM','DateTime'],
7890
[new \DateTime('2000/01/01 UTC'),'Jan 1, 2000, 12:00 AM','2005/01/01 UTC','Jan 1, 2005, 12:00 AM','DateTime'],
7991
['b','"b"','c','"c"','string'],
92+
['30 < 31 (string)' =>new \DateInterval('PT30S'),'30 seconds','+31 seconds','31 seconds', \DateInterval::class],
93+
['30 < 31 (\DateInterval instance)' =>new \DateInterval('PT30S'),'30 seconds',new \DateInterval('PT31S'),'31 seconds', \DateInterval::class],
94+
['-30 < -29' =>$negativeDateInterval,'-30 seconds','-29 seconds','-29 seconds', \DateInterval::class],
8095
];
8196
}
8297

‎src/Symfony/Component/Validator/Tests/Constraints/GreaterThanOrEqualValidatorWithPositiveOrZeroConstraintTest.php‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ public function provideValidComparisons(): array
3838
['0','0'],
3939
['333','0'],
4040
[null,0],
41+
['30 >= 0' =>new \DateInterval('PT30S'),0],
42+
['0 >= 0' =>new \DateInterval('PT0S'),0],
4143
];
4244
}
4345

@@ -46,10 +48,14 @@ public function provideValidComparisons(): array
4648
*/
4749
publicfunctionprovideInvalidComparisons():array
4850
{
51+
$negativeDateInterval =new \DateInterval('PT45S');
52+
$negativeDateInterval->invert =1;
53+
4954
return [
5055
[-1,'-1',0,'0','integer'],
5156
[-2,'-2',0,'0','integer'],
5257
[-2.5,'-2.5',0,'0','integer'],
58+
['-45 < 0' =>$negativeDateInterval,'-45 seconds',0,'0', \DateInterval::class],
5359
];
5460
}
5561

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp