mirror of
https://github.com/Respect/Validation.git
synced 2024-06-18 13:35:06 +02:00
Recreate "Max" rule
The "Max" rule is not a transformation, validating the maximum value in the input against a given rule. Signed-off-by: Henrique Moody <henriquemoody@gmail.com>
This commit is contained in:
parent
81befe8fa1
commit
cea77d2a46
|
@ -51,6 +51,7 @@
|
||||||
- [In](rules/In.md)
|
- [In](rules/In.md)
|
||||||
- [LessThan](rules/LessThan.md)
|
- [LessThan](rules/LessThan.md)
|
||||||
- [LessThanOrEqual](rules/LessThanOrEqual.md)
|
- [LessThanOrEqual](rules/LessThanOrEqual.md)
|
||||||
|
- [Max](rules/Max.md)
|
||||||
- [Min](rules/Min.md)
|
- [Min](rules/Min.md)
|
||||||
|
|
||||||
## Composite
|
## Composite
|
||||||
|
@ -249,6 +250,7 @@
|
||||||
|
|
||||||
- [Call](rules/Call.md)
|
- [Call](rules/Call.md)
|
||||||
- [Each](rules/Each.md)
|
- [Each](rules/Each.md)
|
||||||
|
- [Max](rules/Max.md)
|
||||||
- [Min](rules/Min.md)
|
- [Min](rules/Min.md)
|
||||||
|
|
||||||
## Types
|
## Types
|
||||||
|
@ -359,6 +361,7 @@
|
||||||
- [Lowercase](rules/Lowercase.md)
|
- [Lowercase](rules/Lowercase.md)
|
||||||
- [Luhn](rules/Luhn.md)
|
- [Luhn](rules/Luhn.md)
|
||||||
- [MacAddress](rules/MacAddress.md)
|
- [MacAddress](rules/MacAddress.md)
|
||||||
|
- [Max](rules/Max.md)
|
||||||
- [MaxAge](rules/MaxAge.md)
|
- [MaxAge](rules/MaxAge.md)
|
||||||
- [Mimetype](rules/Mimetype.md)
|
- [Mimetype](rules/Mimetype.md)
|
||||||
- [Min](rules/Min.md)
|
- [Min](rules/Min.md)
|
||||||
|
|
|
@ -36,4 +36,5 @@ See also:
|
||||||
- [Length](Length.md)
|
- [Length](Length.md)
|
||||||
- [LessThan](LessThan.md)
|
- [LessThan](LessThan.md)
|
||||||
- [LessThanOrEqual](LessThanOrEqual.md)
|
- [LessThanOrEqual](LessThanOrEqual.md)
|
||||||
|
- [Max](Max.md)
|
||||||
- [Min](Min.md)
|
- [Min](Min.md)
|
||||||
|
|
|
@ -30,4 +30,5 @@ See also:
|
||||||
- [Between](Between.md)
|
- [Between](Between.md)
|
||||||
- [GreaterThanOrEqual](GreaterThanOrEqual.md)
|
- [GreaterThanOrEqual](GreaterThanOrEqual.md)
|
||||||
- [LessThanOrEqual](LessThanOrEqual.md)
|
- [LessThanOrEqual](LessThanOrEqual.md)
|
||||||
|
- [Max](Max.md)
|
||||||
- [Min](Min.md)
|
- [Min](Min.md)
|
||||||
|
|
|
@ -36,6 +36,7 @@ See also:
|
||||||
- [Length](Length.md)
|
- [Length](Length.md)
|
||||||
- [LessThan](LessThan.md)
|
- [LessThan](LessThan.md)
|
||||||
- [LessThanOrEqual](LessThanOrEqual.md)
|
- [LessThanOrEqual](LessThanOrEqual.md)
|
||||||
|
- [Max](Max.md)
|
||||||
- [MaxAge](MaxAge.md)
|
- [MaxAge](MaxAge.md)
|
||||||
- [Min](Min.md)
|
- [Min](Min.md)
|
||||||
- [MinAge](MinAge.md)
|
- [MinAge](MinAge.md)
|
||||||
|
|
|
@ -33,3 +33,4 @@ See also:
|
||||||
- [Each](Each.md)
|
- [Each](Each.md)
|
||||||
- [Instance](Instance.md)
|
- [Instance](Instance.md)
|
||||||
- [IterableVal](IterableVal.md)
|
- [IterableVal](IterableVal.md)
|
||||||
|
- [Max](Max.md)
|
||||||
|
|
|
@ -30,4 +30,5 @@ See also:
|
||||||
- [Between](Between.md)
|
- [Between](Between.md)
|
||||||
- [GreaterThanOrEqual](GreaterThanOrEqual.md)
|
- [GreaterThanOrEqual](GreaterThanOrEqual.md)
|
||||||
- [LessThanOrEqual](LessThanOrEqual.md)
|
- [LessThanOrEqual](LessThanOrEqual.md)
|
||||||
|
- [Max](Max.md)
|
||||||
- [Min](Min.md)
|
- [Min](Min.md)
|
||||||
|
|
|
@ -35,6 +35,7 @@ See also:
|
||||||
- [GreaterThan](GreaterThan.md)
|
- [GreaterThan](GreaterThan.md)
|
||||||
- [GreaterThanOrEqual](GreaterThanOrEqual.md)
|
- [GreaterThanOrEqual](GreaterThanOrEqual.md)
|
||||||
- [LessThan](LessThan.md)
|
- [LessThan](LessThan.md)
|
||||||
|
- [Max](Max.md)
|
||||||
- [MaxAge](MaxAge.md)
|
- [MaxAge](MaxAge.md)
|
||||||
- [Min](Min.md)
|
- [Min](Min.md)
|
||||||
- [MinAge](MinAge.md)
|
- [MinAge](MinAge.md)
|
||||||
|
|
47
docs/rules/Max.md
Normal file
47
docs/rules/Max.md
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
# Max
|
||||||
|
|
||||||
|
- `Max(Validatable $rule)`
|
||||||
|
|
||||||
|
Validates the maximum value of the input against a given rule.
|
||||||
|
|
||||||
|
```php
|
||||||
|
v::max(v::equals(30))->validate([10, 20, 30]); // true
|
||||||
|
|
||||||
|
v::max(v::between('e', 'g'))->validate(['b', 'd', 'f']); // true
|
||||||
|
|
||||||
|
v::max(v::greaterThan(new DateTime('today')))
|
||||||
|
->validate([new DateTime('yesterday'), new DateTime('tomorrow')]); // true
|
||||||
|
|
||||||
|
v::max(v::greaterThan(15))->validate([4, 8, 12]); // false
|
||||||
|
```
|
||||||
|
|
||||||
|
## Note
|
||||||
|
|
||||||
|
This rule uses [IterableType](IterableType.md) and [NotEmpty](NotEmpty.md) internally. If an input is non-iterable or
|
||||||
|
empty, the validation will fail.
|
||||||
|
|
||||||
|
## Categorization
|
||||||
|
|
||||||
|
- Comparisons
|
||||||
|
- Transformations
|
||||||
|
|
||||||
|
## Changelog
|
||||||
|
|
||||||
|
| Version | Description |
|
||||||
|
|--------:|-----------------------------|
|
||||||
|
| 3.0.0 | Became a transformation |
|
||||||
|
| 2.0.0 | Became always inclusive |
|
||||||
|
| 1.0.0 | Became inclusive by default |
|
||||||
|
| 0.3.9 | Created |
|
||||||
|
|
||||||
|
***
|
||||||
|
See also:
|
||||||
|
|
||||||
|
- [Between](Between.md)
|
||||||
|
- [GreaterThan](GreaterThan.md)
|
||||||
|
- [GreaterThanOrEqual](GreaterThanOrEqual.md)
|
||||||
|
- [IterableType](IterableType.md)
|
||||||
|
- [LessThan](LessThan.md)
|
||||||
|
- [LessThanOrEqual](LessThanOrEqual.md)
|
||||||
|
- [Min](Min.md)
|
||||||
|
- [NotEmpty](NotEmpty.md)
|
|
@ -43,4 +43,5 @@ See also:
|
||||||
- [GreaterThanOrEqual](GreaterThanOrEqual.md)
|
- [GreaterThanOrEqual](GreaterThanOrEqual.md)
|
||||||
- [LessThan](LessThan.md)
|
- [LessThan](LessThan.md)
|
||||||
- [LessThanOrEqual](LessThanOrEqual.md)
|
- [LessThanOrEqual](LessThanOrEqual.md)
|
||||||
|
- [Max](Max.md)
|
||||||
- [NotEmpty](NotEmpty.md)
|
- [NotEmpty](NotEmpty.md)
|
||||||
|
|
|
@ -49,6 +49,7 @@ Version | Description
|
||||||
See also:
|
See also:
|
||||||
|
|
||||||
- [Each](Each.md)
|
- [Each](Each.md)
|
||||||
|
- [Max](Max.md)
|
||||||
- [Min](Min.md)
|
- [Min](Min.md)
|
||||||
- [NoWhitespace](NoWhitespace.md)
|
- [NoWhitespace](NoWhitespace.md)
|
||||||
- [NotBlank](NotBlank.md)
|
- [NotBlank](NotBlank.md)
|
||||||
|
|
|
@ -198,6 +198,8 @@ interface ChainedValidator extends Validatable
|
||||||
|
|
||||||
public function lessThanOrEqual(mixed $compareTo): ChainedValidator;
|
public function lessThanOrEqual(mixed $compareTo): ChainedValidator;
|
||||||
|
|
||||||
|
public function max(Validatable $rule): ChainedValidator;
|
||||||
|
|
||||||
public function maxAge(int $age, ?string $format = null): ChainedValidator;
|
public function maxAge(int $age, ?string $format = null): ChainedValidator;
|
||||||
|
|
||||||
public function min(Validatable $rule): ChainedValidator;
|
public function min(Validatable $rule): ChainedValidator;
|
||||||
|
|
32
library/Rules/Max.php
Normal file
32
library/Rules/Max.php
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Respect\Validation\Rules;
|
||||||
|
|
||||||
|
use Respect\Validation\Message\Template;
|
||||||
|
use Respect\Validation\Result;
|
||||||
|
use Respect\Validation\Rules\Core\FilteredNonEmptyArray;
|
||||||
|
|
||||||
|
use function max;
|
||||||
|
|
||||||
|
#[Template('As the maximum of {{name}},', 'As the maximum of {{name}},')]
|
||||||
|
#[Template('The maximum of', 'The maximum of', self::TEMPLATE_NAMED)]
|
||||||
|
final class Max extends FilteredNonEmptyArray
|
||||||
|
{
|
||||||
|
public const TEMPLATE_NAMED = '__named__';
|
||||||
|
|
||||||
|
/** @param non-empty-array<mixed> $input */
|
||||||
|
protected function evaluateNonEmptyArray(array $input): Result
|
||||||
|
{
|
||||||
|
$result = $this->rule->evaluate(max($input));
|
||||||
|
$template = $this->getName() === null ? self::TEMPLATE_STANDARD : self::TEMPLATE_NAMED;
|
||||||
|
|
||||||
|
return (new Result($result->isValid, $input, $this, [], $template))->withNextSibling($result);
|
||||||
|
}
|
||||||
|
}
|
|
@ -200,6 +200,8 @@ interface StaticValidator
|
||||||
|
|
||||||
public static function lessThanOrEqual(mixed $compareTo): ChainedValidator;
|
public static function lessThanOrEqual(mixed $compareTo): ChainedValidator;
|
||||||
|
|
||||||
|
public static function max(Validatable $rule): ChainedValidator;
|
||||||
|
|
||||||
public static function maxAge(int $age, ?string $format = null): ChainedValidator;
|
public static function maxAge(int $age, ?string $format = null): ChainedValidator;
|
||||||
|
|
||||||
public static function min(Validatable $rule): ChainedValidator;
|
public static function min(Validatable $rule): ChainedValidator;
|
||||||
|
|
100
tests/integration/rules/max.phpt
Normal file
100
tests/integration/rules/max.phpt
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
require 'vendor/autoload.php';
|
||||||
|
|
||||||
|
use Respect\Validation\Validator as v;
|
||||||
|
|
||||||
|
$empty = [];
|
||||||
|
$nonIterable = null;
|
||||||
|
$default = [1, 2, 3];
|
||||||
|
$negative = [-3, -2, -1];
|
||||||
|
|
||||||
|
|
||||||
|
run([
|
||||||
|
// Simple
|
||||||
|
'Non-iterable' => [v::max(v::negative()), $nonIterable],
|
||||||
|
'Empty' => [v::max(v::negative()), $empty],
|
||||||
|
'Default' => [v::max(v::negative()), $default],
|
||||||
|
'Negative' => [v::not(v::max(v::negative())), $negative],
|
||||||
|
'With wrapped name, default' => [v::max(v::negative()->setName('Wrapped'))->setName('Wrapper'), $default],
|
||||||
|
'With wrapper name, default' => [v::max(v::negative())->setName('Wrapper'), $default],
|
||||||
|
'With wrapped name, negative' => [v::not(v::max(v::negative()->setName('Wrapped')))->setName('Wrapper'), $negative],
|
||||||
|
'With wrapper name, negative' => [v::not(v::max(v::negative()))->setName('Wrapper'), $negative],
|
||||||
|
'With template, default' => [v::max(v::negative()), $default, 'The maximum of the value is not what we expect'],
|
||||||
|
]);
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
Non-iterable
|
||||||
|
⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺
|
||||||
|
`null` must be of type iterable
|
||||||
|
- `null` must be of type iterable
|
||||||
|
[
|
||||||
|
'max' => '`null` must be of type iterable',
|
||||||
|
]
|
||||||
|
|
||||||
|
Empty
|
||||||
|
⎺⎺⎺⎺⎺
|
||||||
|
The value must not be empty
|
||||||
|
- The value must not be empty
|
||||||
|
[
|
||||||
|
'max' => 'The value must not be empty',
|
||||||
|
]
|
||||||
|
|
||||||
|
Default
|
||||||
|
⎺⎺⎺⎺⎺⎺⎺
|
||||||
|
As the maximum of `[1, 2, 3]`, 3 must be negative
|
||||||
|
- As the maximum of `[1, 2, 3]`, 3 must be negative
|
||||||
|
[
|
||||||
|
'max' => 'As the maximum of `[1, 2, 3]`, 3 must be negative',
|
||||||
|
]
|
||||||
|
|
||||||
|
Negative
|
||||||
|
⎺⎺⎺⎺⎺⎺⎺⎺
|
||||||
|
As the maximum of `[-3, -2, -1]`, -1 must not be negative
|
||||||
|
- As the maximum of `[-3, -2, -1]`, -1 must not be negative
|
||||||
|
[
|
||||||
|
'max' => 'As the maximum of `[-3, -2, -1]`, -1 must not be negative',
|
||||||
|
]
|
||||||
|
|
||||||
|
With wrapped name, default
|
||||||
|
⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺
|
||||||
|
The maximum of Wrapped must be negative
|
||||||
|
- The maximum of Wrapped must be negative
|
||||||
|
[
|
||||||
|
'Wrapped' => 'The maximum of Wrapped must be negative',
|
||||||
|
]
|
||||||
|
|
||||||
|
With wrapper name, default
|
||||||
|
⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺
|
||||||
|
The maximum of Wrapper must be negative
|
||||||
|
- The maximum of Wrapper must be negative
|
||||||
|
[
|
||||||
|
'Wrapper' => 'The maximum of Wrapper must be negative',
|
||||||
|
]
|
||||||
|
|
||||||
|
With wrapped name, negative
|
||||||
|
⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺
|
||||||
|
The maximum of Wrapped must not be negative
|
||||||
|
- The maximum of Wrapped must not be negative
|
||||||
|
[
|
||||||
|
'Wrapped' => 'The maximum of Wrapped must not be negative',
|
||||||
|
]
|
||||||
|
|
||||||
|
With wrapper name, negative
|
||||||
|
⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺
|
||||||
|
The maximum of Wrapper must not be negative
|
||||||
|
- The maximum of Wrapper must not be negative
|
||||||
|
[
|
||||||
|
'Wrapper' => 'The maximum of Wrapper must not be negative',
|
||||||
|
]
|
||||||
|
|
||||||
|
With template, default
|
||||||
|
⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺
|
||||||
|
The maximum of the value is not what we expect
|
||||||
|
- The maximum of the value is not what we expect
|
||||||
|
[
|
||||||
|
'max' => 'The maximum of the value is not what we expect',
|
||||||
|
]
|
85
tests/unit/Rules/MaxTest.php
Normal file
85
tests/unit/Rules/MaxTest.php
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Respect\Validation\Rules;
|
||||||
|
|
||||||
|
use DateTimeImmutable;
|
||||||
|
use PHPUnit\Framework\Attributes\CoversClass;
|
||||||
|
use PHPUnit\Framework\Attributes\DataProvider;
|
||||||
|
use PHPUnit\Framework\Attributes\Group;
|
||||||
|
use PHPUnit\Framework\Attributes\Test;
|
||||||
|
use Respect\Validation\Test\Rules\Stub;
|
||||||
|
use Respect\Validation\Test\TestCase;
|
||||||
|
|
||||||
|
#[Group('rule')]
|
||||||
|
#[CoversClass(Max::class)]
|
||||||
|
final class MaxTest extends TestCase
|
||||||
|
{
|
||||||
|
#[Test]
|
||||||
|
#[DataProvider('providerForNonIterableValues')]
|
||||||
|
public function itShouldInvalidateNonIterableValues(mixed $input): void
|
||||||
|
{
|
||||||
|
$rule = new Max(Stub::daze());
|
||||||
|
|
||||||
|
self::assertInvalidInput($rule, $input);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @param iterable<mixed> $input */
|
||||||
|
#[Test]
|
||||||
|
#[DataProvider('providerForEmptyIterableValues')]
|
||||||
|
public function itShouldInvalidateEmptyIterableValues(iterable $input): void
|
||||||
|
{
|
||||||
|
$rule = new Max(Stub::daze());
|
||||||
|
|
||||||
|
self::assertInvalidInput($rule, $input);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @param iterable<mixed> $input */
|
||||||
|
#[Test]
|
||||||
|
#[DataProvider('providerForNonEmptyIterableValues')]
|
||||||
|
public function itShouldValidateNonEmptyIterableValuesWhenWrappedRulePasses(iterable $input): void
|
||||||
|
{
|
||||||
|
$rule = new Max(Stub::pass(1));
|
||||||
|
|
||||||
|
self::assertValidInput($rule, $input);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @param iterable<mixed> $input */
|
||||||
|
#[Test]
|
||||||
|
#[DataProvider('providerForMaxValues')]
|
||||||
|
public function itShouldValidateWithTheMaximumValue(iterable $input, mixed $min): void
|
||||||
|
{
|
||||||
|
$wrapped = Stub::pass(1);
|
||||||
|
|
||||||
|
$rule = new Max($wrapped);
|
||||||
|
$rule->evaluate($input);
|
||||||
|
|
||||||
|
self::assertSame($min, $wrapped->inputs[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return array<string, array{iterable<mixed>, mixed}> */
|
||||||
|
public static function providerForMaxValues(): array
|
||||||
|
{
|
||||||
|
$yesterday = new DateTimeImmutable('yesterday');
|
||||||
|
$today = new DateTimeImmutable('today');
|
||||||
|
$tomorrow = new DateTimeImmutable('tomorrow');
|
||||||
|
|
||||||
|
return [
|
||||||
|
'3 DateTime objects' => [[$yesterday, $today, $tomorrow], $tomorrow],
|
||||||
|
'2 DateTime objects' => [[$yesterday, $today], $today],
|
||||||
|
'1 DateTime objects' => [[$yesterday], $yesterday],
|
||||||
|
'3 integers' => [[1, 2, 3], 3],
|
||||||
|
'2 integers' => [[1, 2], 2],
|
||||||
|
'1 integer' => [[1], 1],
|
||||||
|
'3 characters' => [['a', 'b', 'c'], 'c'],
|
||||||
|
'2 characters' => [['a', 'b'], 'b'],
|
||||||
|
'1 character' => [['a'], 'a'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue