Skip to content

Commit

Permalink
Merge pull request #46 from kavinsky/feature/date-min-max-rules
Browse files Browse the repository at this point in the history
Added support for dates in conjunction with min/max/between rules
  • Loading branch information
frankdekker authored Mar 9, 2023
2 parents 9c1df52 + 6dceea7 commit f95d2dd
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 10 deletions.
15 changes: 14 additions & 1 deletion docs/available-shorthands.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,16 @@ The field under validation must be a PHP array.
Arguments: `<digit>,<digit>`

The constraint has different implementations based on the value type.
- If the value has a date constraint (date, datetime or datetime_format), the `<digit>` arguments accepts now `DateTime`
allowed formats. The value must be between than the supplied `<digit>` 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.
Expand Down Expand Up @@ -87,23 +91,32 @@ Note: can also be written as `int`
Argument: `<digit>`

The constraint has different implementations based on the value type.
- If the value has a date constraint (date, datetime or datetime_format), the `<digit>` 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: `<digit>`
Argument: `<value>`

The constraint has different implementations based on the value type.
- If the value has a date constraint (date, datetime or datetime_format), the `<digit>` argument accepts now `DateTime`
allowed formats and the value must be greater or equal than the supplied `<digit>` 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`.
Expand Down
33 changes: 24 additions & 9 deletions src/Constraint/ConstraintResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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)]);
}
}
8 changes: 8 additions & 0 deletions tests/Integration/FieldValidationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down Expand Up @@ -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];
Expand Down
12 changes: 12 additions & 0 deletions tests/Unit/Constraint/ConstraintResolverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,18 @@ 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'])]
];
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'])]];
Expand Down

0 comments on commit f95d2dd

Please sign in to comment.