mirror of
https://github.com/Respect/Validation.git
synced 2024-06-08 16:52:16 +02:00
Refactor "Sorted" rule
The sorted rule accepts a callback on its constructor that may be used to filter values from inside the input. However, with the "Call" rule one can archive almost the same result. Besides that particular characteristic, its constructor accepts a boolean value to determine whether the sorting is ascending or descending. This commit will remove the callback from the constructor and replace the boolean by a string which can be "ASC" or "DESC." Along with those changes, this change will make a few more improvements: - Make the exception message specific about the sorting direction; - Allow the rule to validate also strings; - Update documentation. Co-authored-by: Danilo Correa <danilosilva87@gmail.com> Signed-off-by: Henrique Moody <henriquemoody@gmail.com>
This commit is contained in:
parent
5444ab5b0a
commit
1f6c821fb6
|
@ -1,48 +1,48 @@
|
|||
# Ordered
|
||||
# Sorted
|
||||
|
||||
- `Sorted(callable $fn = null, bool $ascending = true)`
|
||||
- `Sorted(string $direction)`
|
||||
|
||||
Validates if the input is Sorted
|
||||
Validates whether the input is sorted in a certain order or not.
|
||||
|
||||
```php
|
||||
v::sorted()->validate([1,2,3]); // true
|
||||
v::sorted()->validate([1,6,3]); // false
|
||||
v::sorted(null, false)->validate([3,2,1]); // true
|
||||
v::sorted(function($x){
|
||||
return $x['key'];
|
||||
})->validate([
|
||||
[
|
||||
'key' => 1,
|
||||
],
|
||||
[
|
||||
'key' => 5,
|
||||
],
|
||||
[
|
||||
'key' => 9,
|
||||
],
|
||||
]); // true
|
||||
v::sorted(function($x){
|
||||
return $x['key'];
|
||||
})->validate([
|
||||
[
|
||||
'key' => 1,
|
||||
],
|
||||
[
|
||||
'key' => 7,
|
||||
],
|
||||
[
|
||||
'key' => 4,
|
||||
],
|
||||
]); // false
|
||||
v::sorted('ASC')->validate([1, 2, 3]); // true
|
||||
v::sorted('ASC')->validate('ABC'); // true
|
||||
v::sorted('DESC')->validate([3, 2, 1]); // true
|
||||
v::sorted('ASC')->validate([]); // true
|
||||
v::sorted('ASC')->validate([1]); // true
|
||||
```
|
||||
|
||||
You can also combine [Call](Call.md) to create custom validations:
|
||||
|
||||
```php
|
||||
v::call(
|
||||
static function (array $input): array {
|
||||
return array_column($input, 'key');
|
||||
},
|
||||
v::sorted('ASC')
|
||||
)->validate([
|
||||
['key' => 1],
|
||||
['key' => 5],
|
||||
['key' => 9],
|
||||
]); // true
|
||||
|
||||
v::call('strval', v::sorted('DESC'))->validate(4321); // true
|
||||
|
||||
v::call('iterator_to_array', v::sorted())->validate(new ArrayIterator([1, 7, 4])); // false
|
||||
```
|
||||
|
||||
## Changelog
|
||||
|
||||
Version | Description
|
||||
--------|-------------
|
||||
2.0.0 | Add support for strings
|
||||
2.0.0 | Do not use array keys to sort
|
||||
2.0.0 | Use sorting direction instead of boolean value
|
||||
2.0.0 | Do not accept callback in the constructor
|
||||
1.1.1 | Created
|
||||
|
||||
***
|
||||
See also:
|
||||
|
||||
- [Call](Call.md)
|
||||
- [ArrayVal](ArrayVal.md)
|
||||
|
|
|
@ -13,21 +13,36 @@ declare(strict_types=1);
|
|||
|
||||
namespace Respect\Validation\Exceptions;
|
||||
|
||||
use Respect\Validation\Rules\Sorted;
|
||||
|
||||
/**
|
||||
* @author Henrique Moody <henriquemoody@gmail.com>
|
||||
* @author Mikhail Vyrtsev <reeywhaar@gmail.com>
|
||||
*/
|
||||
final class SortedException extends ValidationException
|
||||
{
|
||||
public const ASCENDING = 'ascending';
|
||||
public const DESCENDING = 'descending';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $defaultTemplates = [
|
||||
self::MODE_DEFAULT => [
|
||||
self::STANDARD => '{{name}} must be ordered',
|
||||
self::ASCENDING => '{{name}} must be sorted in ascending order',
|
||||
self::DESCENDING => '{{name}} must be sorted in descending order',
|
||||
],
|
||||
self::MODE_NEGATIVE => [
|
||||
self::STANDARD => '{{name}} must not be ordered',
|
||||
self::ASCENDING => '{{name}} must not be sorted in ascending order',
|
||||
self::DESCENDING => '{{name}} must not be sorted in descending order',
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function chooseTemplate(): string
|
||||
{
|
||||
return $this->getParam('direction') === Sorted::ASCENDING ? self::ASCENDING : self::DESCENDING;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,30 +13,39 @@ declare(strict_types=1);
|
|||
|
||||
namespace Respect\Validation\Rules;
|
||||
|
||||
use Respect\Validation\Exceptions\ComponentException;
|
||||
use function array_values;
|
||||
use function count;
|
||||
use function is_array;
|
||||
use function is_string;
|
||||
use function sprintf;
|
||||
use function str_split;
|
||||
|
||||
/**
|
||||
* Validates whether the input is sorted in a certain order or not.
|
||||
*
|
||||
* @author Henrique Moody <henriquemoody@gmail.com>
|
||||
* @author Mikhail Vyrtsev <reeywhaar@gmail.com>
|
||||
*/
|
||||
final class Sorted extends AbstractRule
|
||||
{
|
||||
/**
|
||||
* @var callable
|
||||
*/
|
||||
private $fn = null;
|
||||
public const ASCENDING = 'ASC';
|
||||
public const DESCENDING = 'DESC';
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
* @var string
|
||||
*/
|
||||
private $ascending = true;
|
||||
private $direction;
|
||||
|
||||
public function __construct(?callable $fn = null, bool $ascending = true)
|
||||
public function __construct(string $direction)
|
||||
{
|
||||
$this->fn = $fn ?? static function ($x) {
|
||||
return $x;
|
||||
};
|
||||
$this->ascending = $ascending;
|
||||
if ($direction !== self::ASCENDING && $direction !== self::DESCENDING) {
|
||||
throw new ComponentException(
|
||||
sprintf('Direction should be either "%s" or "%s"', self::ASCENDING, self::DESCENDING)
|
||||
);
|
||||
}
|
||||
|
||||
$this->direction = $direction;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -44,18 +53,45 @@ final class Sorted extends AbstractRule
|
|||
*/
|
||||
public function validate($input): bool
|
||||
{
|
||||
$count = count($input);
|
||||
if ($count < 2) {
|
||||
return true;
|
||||
if (!is_array($input) && !is_string($input)) {
|
||||
return false;
|
||||
}
|
||||
for ($i = 1; $i < $count; ++$i) {
|
||||
if (($this->ascending && ($this->fn)($input[$i]) < ($this->fn)($input[$i - 1]))
|
||||
|| (!$this->ascending && ($this->fn)($input[$i]) > ($this->fn)($input[$i - 1]))
|
||||
) {
|
||||
|
||||
$values = $this->getValues($input);
|
||||
$count = count($values);
|
||||
for ($position = 1; $position < $count; ++$position) {
|
||||
if (!$this->isSorted($values[$position], $values[$position - 1])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $current
|
||||
* @param mixed $last
|
||||
*/
|
||||
private function isSorted($current, $last): bool
|
||||
{
|
||||
if ($this->direction === self::ASCENDING) {
|
||||
return $current > $last;
|
||||
}
|
||||
|
||||
return $current < $last;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|mixed[] $input
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
private function getValues($input): array
|
||||
{
|
||||
if (is_array($input)) {
|
||||
return array_values($input);
|
||||
}
|
||||
|
||||
return str_split($input);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -148,6 +148,7 @@ use function count;
|
|||
* @method static Validator sf(Constraint $constraint, ValidatorInterface $validator = null)
|
||||
* @method static Validator size(string $minSize = null, string $maxSize = null)
|
||||
* @method static Validator slug()
|
||||
* @method static Validator sorted(string $direction)
|
||||
* @method static Validator space(string ...$additionalChars)
|
||||
* @method static Validator startsWith($startValue, bool $identical = false)
|
||||
* @method static Validator stringType()
|
||||
|
|
71
tests/integration/rules/sorted.phpt
Normal file
71
tests/integration/rules/sorted.phpt
Normal file
|
@ -0,0 +1,71 @@
|
|||
--CREDITS--
|
||||
Danilo Correa <danilosilva87@gmail.com>
|
||||
Henrique Moody <henriquemoody@gmail.com>
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require 'vendor/autoload.php';
|
||||
|
||||
use Respect\Validation\Exceptions\NestedValidationException;
|
||||
use Respect\Validation\Exceptions\SortedException;
|
||||
use Respect\Validation\Validator as v;
|
||||
|
||||
try {
|
||||
v::sorted('ASC')->check([1, 3, 2]);
|
||||
} catch (SortedException $exception) {
|
||||
echo $exception->getMessage().PHP_EOL;
|
||||
}
|
||||
|
||||
try {
|
||||
v::sorted('DESC')->check([1, 2, 3]);
|
||||
} catch (SortedException $exception) {
|
||||
echo $exception->getMessage().PHP_EOL;
|
||||
}
|
||||
|
||||
try {
|
||||
v::not(v::sorted('ASC'))->check([1, 2, 3]);
|
||||
} catch (SortedException $exception) {
|
||||
echo $exception->getMessage().PHP_EOL;
|
||||
}
|
||||
|
||||
try {
|
||||
v::not(v::sorted('DESC'))->check([3, 2, 1]);
|
||||
} catch (SortedException $exception) {
|
||||
echo $exception->getMessage().PHP_EOL;
|
||||
}
|
||||
|
||||
try {
|
||||
v::sorted('ASC')->assert([3, 2, 1]);
|
||||
} catch (NestedValidationException $exception) {
|
||||
echo $exception->getFullMessage().PHP_EOL;
|
||||
}
|
||||
|
||||
try {
|
||||
v::sorted('DESC')->assert([1, 2, 3]);
|
||||
} catch (NestedValidationException $exception) {
|
||||
echo $exception->getFullMessage().PHP_EOL;
|
||||
}
|
||||
|
||||
try {
|
||||
v::not(v::sorted('ASC'))->assert([1, 2, 3]);
|
||||
} catch (NestedValidationException $exception) {
|
||||
echo $exception->getFullMessage().PHP_EOL;
|
||||
}
|
||||
|
||||
try {
|
||||
v::not(v::sorted('DESC'))->assert([3, 2, 1]);
|
||||
} catch (NestedValidationException $exception) {
|
||||
echo $exception->getFullMessage().PHP_EOL;
|
||||
}
|
||||
?>
|
||||
--EXPECT--
|
||||
`{ 1, 3, 2 }` must be sorted in ascending order
|
||||
`{ 1, 2, 3 }` must be sorted in descending order
|
||||
`{ 1, 2, 3 }` must not be sorted in ascending order
|
||||
`{ 3, 2, 1 }` must not be sorted in descending order
|
||||
- `{ 3, 2, 1 }` must be sorted in ascending order
|
||||
- `{ 1, 2, 3 }` must be sorted in descending order
|
||||
- `{ 1, 2, 3 }` must not be sorted in ascending order
|
||||
- `{ 3, 2, 1 }` must not be sorted in descending order
|
|
@ -13,133 +13,62 @@ declare(strict_types=1);
|
|||
|
||||
namespace Respect\Validation\Rules;
|
||||
|
||||
use Respect\Validation\Test\TestCase;
|
||||
use Respect\Validation\Exceptions\ComponentException;
|
||||
use Respect\Validation\Test\RuleTestCase;
|
||||
|
||||
/**
|
||||
* @group rule
|
||||
* @covers \Respect\Validation\Exceptions\SortedException
|
||||
* @group rules
|
||||
*
|
||||
* @covers \Respect\Validation\Rules\Sorted
|
||||
*
|
||||
* @author Gabriel Caruso <carusogabriel34@gmail.com>
|
||||
* @author Henrique Moody <henriquemoody@gmail.com>
|
||||
* @author Mikhail Vyrtsev <reeywhaar@gmail.com>
|
||||
*/
|
||||
final class SortedTest extends TestCase
|
||||
final class SortedTest extends RuleTestCase
|
||||
{
|
||||
/**
|
||||
* @test
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function passes(): void
|
||||
public function providerForValidInput(): array
|
||||
{
|
||||
$arr = [1, 2, 3];
|
||||
$rule = new Sorted();
|
||||
|
||||
self::assertTrue($rule->validate($arr));
|
||||
$rule->assert($arr);
|
||||
$rule->check($arr);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function passesWithEqualValues(): void
|
||||
{
|
||||
$arr = [1, 2, 2, 3];
|
||||
$rule = new Sorted();
|
||||
|
||||
self::assertTrue($rule->validate($arr));
|
||||
$rule->assert($arr);
|
||||
$rule->check($arr);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Respect\Validation\Exceptions\SortedException
|
||||
*
|
||||
* @test
|
||||
*/
|
||||
public function notPasses(): void
|
||||
{
|
||||
$arr = [1, 2, 4, 3];
|
||||
$rule = new Sorted();
|
||||
|
||||
self::assertFalse($rule->validate($arr));
|
||||
$rule->check($arr);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function passesDescending(): void
|
||||
{
|
||||
$arr = [10, 9, 8];
|
||||
$rule = new Sorted(null, false);
|
||||
|
||||
self::assertTrue($rule->validate($arr));
|
||||
$rule->assert($arr);
|
||||
$rule->check($arr);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function passesDescendingWithEqualValues(): void
|
||||
{
|
||||
$arr = [10, 9, 9, 8];
|
||||
$rule = new Sorted(null, false);
|
||||
|
||||
self::assertTrue($rule->validate($arr));
|
||||
$rule->assert($arr);
|
||||
$rule->check($arr);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function passesByFunction(): void
|
||||
{
|
||||
$arr = [
|
||||
[
|
||||
'key' => 1,
|
||||
],
|
||||
[
|
||||
'key' => 2,
|
||||
],
|
||||
[
|
||||
'key' => 5,
|
||||
],
|
||||
return [
|
||||
'empty' => [new Sorted('ASC'), []],
|
||||
'one item' => [new Sorted('ASC'), [1]],
|
||||
'one character' => [new Sorted('ASC'), 'z'],
|
||||
'ASC array-sequence' => [new Sorted('ASC'), [1, 3, 5]],
|
||||
'ASC sequence in associative array' => [new Sorted('ASC'), ['foo' => 1, 'bar' => 3, 'baz' => 5]],
|
||||
'ASC string-sequence' => [new Sorted('ASC'), 'ABCD'],
|
||||
'DESC array-sequence ' => [new Sorted('DESC'), [10, 9, 8]],
|
||||
'DESC string-sequence ' => [new Sorted('DESC'), 'zyx'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function providerForInvalidInput(): array
|
||||
{
|
||||
return [
|
||||
'duplicate' => [new Sorted('ASC'), [1, 1, 1]],
|
||||
'wrong ASC array-sequence' => [new Sorted('ASC'), [1, 3, 2]],
|
||||
'wrong ASC string-sequence' => [new Sorted('ASC'), 'xzy'],
|
||||
'wrong DESC array-sequence' => [new Sorted('DESC'), [1, 3, 2]],
|
||||
'wrong DESC string-sequence' => [new Sorted('DESC'), 'jml'],
|
||||
'DESC array-sequence with ASC validation' => [new Sorted('ASC'), [3, 2, 1]],
|
||||
'DESC string-sequence with ASC validation' => [new Sorted('ASC'), '321'],
|
||||
'ASC array-sequence with DESC validation' => [new Sorted('DESC'), [1, 2, 3]],
|
||||
'ASC string-sequence with DESC validation' => [new Sorted('DESC'), 'abc'],
|
||||
];
|
||||
$rule = new Sorted(static function ($x) {
|
||||
return $x['key'];
|
||||
});
|
||||
|
||||
self::assertTrue($rule->validate($arr));
|
||||
$rule->assert($arr);
|
||||
$rule->check($arr);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Respect\Validation\Exceptions\SortedException
|
||||
*
|
||||
* @test
|
||||
*/
|
||||
public function notPassesByFunction(): void
|
||||
public function itShouldNotAcceptWrongSortingDirection(): void
|
||||
{
|
||||
$arr = [
|
||||
[
|
||||
'key' => 1,
|
||||
],
|
||||
[
|
||||
'key' => 8,
|
||||
],
|
||||
[
|
||||
'key' => 5,
|
||||
],
|
||||
];
|
||||
$rule = new Sorted(static function ($x) {
|
||||
return $x['key'];
|
||||
});
|
||||
$this->expectExceptionObject(new ComponentException('Direction should be either "ASC" or "DESC"'));
|
||||
|
||||
self::assertFalse($rule->validate($arr));
|
||||
$rule->check($arr);
|
||||
new Sorted('something');
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue