From 2e29152aa03d6d96b85d125cde9879ba7de0219e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacio=20Mu=C3=B1oz=20Fernandez?= Date: Thu, 9 Mar 2023 12:54:16 +0100 Subject: [PATCH 1/5] feat: Added support for dates in conjunction with min/max rules --- src/Constraint/ConstraintResolver.php | 33 ++++++++++++++----- .../Constraint/ConstraintResolverTest.php | 8 +++++ 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/src/Constraint/ConstraintResolver.php b/src/Constraint/ConstraintResolver.php index 2044185..c4cbcac 100644 --- a/src/Constraint/ConstraintResolver.php +++ b/src/Constraint/ConstraintResolver.php @@ -93,11 +93,11 @@ private function resolveConstraint(RuleList $ruleList, Rule $rule): Constraint case Rule::RULE_FILLED: return new Assert\NotBlank(['allowNull' => $ruleList->hasRule(Rule::RULE_NULLABLE)]); case Rule::RULE_MIN: - return $this->resolveMinConstraint($rule, $ruleList->hasRule([Rule::RULE_INTEGER, Rule::RULE_FLOAT])); + return $this->resolveMinConstraint($rule, $ruleList); case Rule::RULE_MAX: - return $this->resolveMaxConstraint($rule, $ruleList->hasRule([Rule::RULE_INTEGER, Rule::RULE_FLOAT])); + return $this->resolveMaxConstraint($rule, $ruleList); case Rule::RULE_BETWEEN: - return $this->resolveBetweenConstraint($rule, $ruleList->hasRule([Rule::RULE_INTEGER, Rule::RULE_FLOAT])); + return $this->resolveBetweenConstraint($rule, $ruleList); } throw new InvalidRuleException( @@ -108,33 +108,48 @@ private function resolveConstraint(RuleList $ruleList, Rule $rule): Constraint /** * @throws InvalidRuleException */ - private function resolveMinConstraint(Rule $rule, bool $isNumeric): Constraint + private function resolveMinConstraint(Rule $rule, RuleList $ruleList): Constraint { - if ($isNumeric) { + if ($ruleList->hasRule([Rule::RULE_DATE, Rule::RULE_DATETIME, Rule::RULE_DATE_FORMAT])) { + return new Assert\GreaterThanOrEqual($rule->getParameter(0)); + } + + if ($ruleList->hasRule([Rule::RULE_INTEGER, Rule::RULE_FLOAT])) { return new Assert\GreaterThanOrEqual($rule->getIntParam(0)); } + return new Assert\Length(['min' => $rule->getIntParam(0)]); } /** * @throws InvalidRuleException */ - private function resolveMaxConstraint(Rule $rule, bool $isNumeric): Constraint + private function resolveMaxConstraint(Rule $rule, RuleList $ruleList): Constraint { - if ($isNumeric) { + if ($ruleList->hasRule([Rule::RULE_DATE, Rule::RULE_DATETIME, Rule::RULE_DATE_FORMAT])) { + return new Assert\LessThanOrEqual($rule->getParameter(0)); + } + + if ($ruleList->hasRule([Rule::RULE_INTEGER, Rule::RULE_FLOAT])) { return new Assert\LessThanOrEqual($rule->getIntParam(0)); } + return new Assert\Length(['max' => $rule->getIntParam(0)]); } /** * @throws InvalidRuleException */ - private function resolveBetweenConstraint(Rule $rule, bool $isNumeric): Constraint + private function resolveBetweenConstraint(Rule $rule, RuleList $ruleList): Constraint { - if ($isNumeric) { + if ($ruleList->hasRule([Rule::RULE_DATE, Rule::RULE_DATETIME, Rule::RULE_DATE_FORMAT])) { + return new Assert\Range(['min' => $rule->getParameter(0), 'max' => $rule->getParameter(1)]); + } + + if ($ruleList->hasRule([Rule::RULE_INTEGER, Rule::RULE_FLOAT])) { return new Assert\Range(['min' => $rule->getIntParam(0), 'max' => $rule->getIntParam(1)]); } + return new Assert\Length(['min' => $rule->getIntParam(0), 'max' => $rule->getIntParam(1)]); } } diff --git a/tests/Unit/Constraint/ConstraintResolverTest.php b/tests/Unit/Constraint/ConstraintResolverTest.php index 5dd75c3..d6498f0 100644 --- a/tests/Unit/Constraint/ConstraintResolverTest.php +++ b/tests/Unit/Constraint/ConstraintResolverTest.php @@ -97,6 +97,14 @@ public function dataProvider(): Generator yield 'date' => [[new Assert\Date(), new Assert\NotNull()], [new Rule('date')]]; yield 'datetime' => [[new Assert\DateTime(), new Assert\NotNull()], [new Rule('datetime')]]; yield 'date_format' => [[new Assert\DateTime(['format' => 'd/m/Y']), new Assert\NotNull()], [new Rule('date_format', ['d/m/Y'])]]; + yield 'date min' => [ + [new Assert\Date(), new Assert\GreaterThanOrEqual('now'), new Assert\NotNull()], + [new Rule('date'), new Rule('min', ['now'])] + ]; + yield 'date max' => [ + [new Assert\Date(), new Assert\LessThanOrEqual('now'), new Assert\NotNull()], + [new Rule('date'), new Rule('max', ['now'])] + ]; // min/max string or array lengths yield 'min length' => [[new Assert\Length(['min' => 10]), new Assert\NotNull()], [new Rule('min', ['10'])]]; From 5d64d79b5af13367b9b925e69543390b63be15c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacio=20Mu=C3=B1oz=20Fernandez?= Date: Thu, 9 Mar 2023 12:56:07 +0100 Subject: [PATCH 2/5] Added tests for date between --- tests/Unit/Constraint/ConstraintResolverTest.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/Unit/Constraint/ConstraintResolverTest.php b/tests/Unit/Constraint/ConstraintResolverTest.php index d6498f0..c1511e7 100644 --- a/tests/Unit/Constraint/ConstraintResolverTest.php +++ b/tests/Unit/Constraint/ConstraintResolverTest.php @@ -105,6 +105,10 @@ public function dataProvider(): Generator [new Assert\Date(), new Assert\LessThanOrEqual('now'), new Assert\NotNull()], [new Rule('date'), new Rule('max', ['now'])] ]; + yield 'date between' => [ + [new Assert\Date(), new Assert\Range(['min' => '-10 days', 'max' => '+10 days']), new Assert\NotNull()], + [new Rule('date'), new Rule('between', ['-10 days', '+10 days'])] + ]; // min/max string or array lengths yield 'min length' => [[new Assert\Length(['min' => 10]), new Assert\NotNull()], [new Rule('min', ['10'])]]; From c75f11e5b5f263f933b64b7d406b18383ac90472 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacio=20Mu=C3=B1oz=20Fernandez?= Date: Thu, 9 Mar 2023 15:35:38 +0100 Subject: [PATCH 3/5] Added documentation for new min, max, between behaviours --- docs/available-shorthands.md | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/docs/available-shorthands.md b/docs/available-shorthands.md index efb86b4..5658ae1 100644 --- a/docs/available-shorthands.md +++ b/docs/available-shorthands.md @@ -32,16 +32,20 @@ The field under validation must be a PHP array. Arguments: `,` The constraint has different implementations based on the value type. +- If the value has a date constraint (date, datetime or datetime_format), the `` arguments accepts now `DateTime` + allowed formats. The value must be between than the supplied `` arguments. + More information in the [Symfony Validation documentation](https://symfony.com/doc/current/reference/constraints/Range.html#date-ranges). - If the value has a numeric constraint (integer or float), it must lie between the two values. - Otherwise, the length of the value must be between the supplied values. Example: - string must have minimum length of 2 and maximum length of 6: `between:2,6` - integer must have a value between 2 and 6 or less: `integer|between:2,6` +- date must be between `2010-01-01` and `2011-01-01`: `date|between:2010-01-01,2011-01-01` ## boolean The value must be bool or castable to bool. -- allowed `true` values: `1, '1', 'on', true` +~~- allowed `true` values: `1, '1', 'on', true`~~ - allowed `false` values: `0, '0', 'off', false` Note: can also be written as `bool` @@ -87,23 +91,32 @@ Note: can also be written as `int` Argument: `` The constraint has different implementations based on the value type. +- If the value has a date constraint (date, datetime or datetime_format), the `` argument accepts now `DateTime` + allowed formats and the value must be less or equal than the supplied argument. + More information in the [Symfony Validation documentation](https://symfony.com/doc/current/reference/constraints/LessThanOrEqual.html#comparing-dates). - If the value has a numeric constraint (integer or float), it must be smaller than the supplied value. - Otherwise, the length of the value must be smaller than the supplied value. + Example: - string with maximum length of 6: `max:6` - integer which has to be 6 or less: `integer|max:6` + - limit the given date by: `date|max:+10 days` ## min -Argument: `` +Argument: `` The constraint has different implementations based on the value type. +- If the value has a date constraint (date, datetime or datetime_format), the `` argument accepts now `DateTime` + allowed formats and the value must be greater or equal than the supplied `` argument. + More information in the [Symfony Validation documentation](https://symfony.com/doc/current/reference/constraints/GreaterThan.html#comparing-dates). - If the value has a numeric constraint (integer or float), it must be bigger than the supplied value. - Otherwise, the length of the value must be bigger than the supplied value. Example: - string with minimum length of 6: `min:6` - integer which has to be 6 or higher: `integer|min:6` +- limit the given date by: `date|min:now` ## nullable The value can be `null`. From 137c26e7d8941e3bf1c9b4d4dcf20841a45b2f16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacio=20Mu=C3=B1oz=20Fernandez?= Date: Thu, 9 Mar 2023 15:41:57 +0100 Subject: [PATCH 4/5] Added integration tests for new date min, max, between behaviours --- tests/Integration/FieldValidationTest.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/Integration/FieldValidationTest.php b/tests/Integration/FieldValidationTest.php index 7af03e8..4dc49e0 100644 --- a/tests/Integration/FieldValidationTest.php +++ b/tests/Integration/FieldValidationTest.php @@ -122,6 +122,7 @@ public function dataProviderRequiredFields(): Generator yield "required + string max with int: true" => ['required|max:1', 9, true]; yield "required + string type with int: true" => ['required|string', 9, false]; + // field should be array yield "required + array: true" => ['required|array', [], true]; yield "required + array: false A" => ['required|array', 5, false]; @@ -182,6 +183,13 @@ public function dataProviderRequiredFields(): Generator yield "required + datetime: false" => ['required|datetime', '12:15:59 2020-01-01', false]; yield "required + date_format: true" => ['required|date_format:d/m/Y', '01/01/2020', true]; yield "required + date_format: false" => ['required|date_format:Y-m-d', '01-01-2020', false]; + yield "required + date + min '2010-01-01': true" => ['required|date|min:2010-01-01', '2010-01-01', true]; + yield "required + date + min '2010-01-01': false" => ['required|date|min:2010-01-01', '2009-12-31', false]; + yield "required + date + max '2010-01-01': true" => ['required|date|max:2010-01-01', '2009-12-31', true]; + yield "required + date + max '2010-01-01': false" => ['required|date|max:2010-01-01', '2010-01-02', false]; + yield "required + date + min '2009-12-30' + max '2010-01-01': true" => ['required|date|min:2009-12-30|max:2010-01-01', '2009-12-31', true]; + yield "required + date + between '2009-12-30' '2010-01-01': true" => ['required|date|min:2009-12-30|max:2010-01-01', '2009-12-31', true]; + yield "required + date + between '2009-12-30' '2010-01-01': false" => ['required|date|min:2009-12-30|max:2010-01-01', '2009-11-30', false]; // field should be email yield "required + email: true" => ['required|email', 'test@example.com', true]; From 6dceea77c047496bbb01600d488ca032f7f2466e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacio=20Mu=C3=B1oz=20Fernandez?= Date: Thu, 9 Mar 2023 16:51:58 +0100 Subject: [PATCH 5/5] fix: fixed unwanted change in available-shorthands doc --- docs/available-shorthands.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/available-shorthands.md b/docs/available-shorthands.md index 5658ae1..0877190 100644 --- a/docs/available-shorthands.md +++ b/docs/available-shorthands.md @@ -45,7 +45,7 @@ Example: ## boolean The value must be bool or castable to bool. -~~- allowed `true` values: `1, '1', 'on', true`~~ +- allowed `true` values: `1, '1', 'on', true` - allowed `false` values: `0, '0', 'off', false` Note: can also be written as `bool`