mirror of
https://github.com/Respect/Validation.git
synced 2024-06-07 16:22:16 +02:00
Create "Lazy" rule
This rule resembles a proper replacement for the old "KeyValue" rule
rather than the "LazyConsecutive" rule[1]. I will soon delete the
"LazyConsecutive" rule and replace it with something else.
[1]: 41245f663f
Signed-off-by: Henrique Moody <henriquemoody@gmail.com>
This commit is contained in:
parent
df16ddaf48
commit
78715fb844
|
@ -39,6 +39,7 @@
|
|||
- [Call](rules/Call.md)
|
||||
- [CallableType](rules/CallableType.md)
|
||||
- [Callback](rules/Callback.md)
|
||||
- [Lazy](rules/Lazy.md)
|
||||
- [LazyConsecutive](rules/LazyConsecutive.md)
|
||||
|
||||
## Comparisons
|
||||
|
@ -163,6 +164,7 @@
|
|||
- [Each](rules/Each.md)
|
||||
- [Key](rules/Key.md)
|
||||
- [KeySet](rules/KeySet.md)
|
||||
- [Lazy](rules/Lazy.md)
|
||||
- [LazyConsecutive](rules/LazyConsecutive.md)
|
||||
- [NoneOf](rules/NoneOf.md)
|
||||
- [Not](rules/Not.md)
|
||||
|
@ -359,6 +361,7 @@
|
|||
- [KeyOptional](rules/KeyOptional.md)
|
||||
- [KeySet](rules/KeySet.md)
|
||||
- [LanguageCode](rules/LanguageCode.md)
|
||||
- [Lazy](rules/Lazy.md)
|
||||
- [LazyConsecutive](rules/LazyConsecutive.md)
|
||||
- [LeapDate](rules/LeapDate.md)
|
||||
- [LeapYear](rules/LeapYear.md)
|
||||
|
|
|
@ -58,5 +58,6 @@ See also:
|
|||
|
||||
- [Callback](Callback.md)
|
||||
- [Each](Each.md)
|
||||
- [Lazy](Lazy.md)
|
||||
- [LazyConsecutive](LazyConsecutive.md)
|
||||
- [Sorted](Sorted.md)
|
||||
|
|
|
@ -30,6 +30,7 @@ See also:
|
|||
- [Callback](Callback.md)
|
||||
- [FloatType](FloatType.md)
|
||||
- [IntType](IntType.md)
|
||||
- [Lazy](Lazy.md)
|
||||
- [NullType](NullType.md)
|
||||
- [Number](Number.md)
|
||||
- [ObjectType](ObjectType.md)
|
||||
|
|
41
docs/rules/Lazy.md
Normal file
41
docs/rules/Lazy.md
Normal file
|
@ -0,0 +1,41 @@
|
|||
# Lazy
|
||||
|
||||
- `Lazy(callable(mixed $input): Validatable $ruleCreator)`
|
||||
|
||||
Validates the input using a rule that is created from a callback.
|
||||
|
||||
This rule is particularly useful when creating rules that rely on the input. A good example is validating whether a
|
||||
`confirmation` field matches the `password` field when processing data from a form.
|
||||
|
||||
```php
|
||||
v::key('confirmation', v::equals($_POST['password'] ?? null))->validate($_POST);
|
||||
```
|
||||
|
||||
The issue with the code is that it’s hard to reuse because you’re relying upon the input itself (`$_POST`). That means
|
||||
you can create a chain of rules and use it everywhere.
|
||||
|
||||
The `lazy()` rule makes this job much simpler and more elegantly:
|
||||
|
||||
```php
|
||||
v::lazy(static fn($input) => v::key('confirmation', v::equals($input['password'] ?? null)))->validate($_POST);
|
||||
```
|
||||
|
||||
The code above is similar to the first example, but the biggest difference is that the creation of the rule doesn't rely
|
||||
on the input itself (`$_POST`), but it will use any input that’s given to the rule
|
||||
|
||||
## Categorization
|
||||
|
||||
- Callables
|
||||
- Nesting
|
||||
|
||||
## Changelog
|
||||
|
||||
| Version | Description |
|
||||
|--------:|-------------------------|
|
||||
| 3.0.0 | Created from `KeyValue` |
|
||||
|
||||
***
|
||||
See also:
|
||||
|
||||
- [Call](Call.md)
|
||||
- [CallableType](CallableType.md)
|
|
@ -172,6 +172,9 @@ interface ChainedValidator extends Validatable
|
|||
|
||||
public function keySet(Validatable $rule, Validatable ...$rules): ChainedValidator;
|
||||
|
||||
/** @param callable(mixed): Validatable $ruleCreator */
|
||||
public function lazy(callable $ruleCreator): ChainedValidator;
|
||||
|
||||
public function lazyConsecutive(callable $ruleCreator, callable ...$ruleCreators): ChainedValidator;
|
||||
|
||||
/** @param "alpha-2"|"alpha-3" $set */
|
||||
|
|
|
@ -174,6 +174,9 @@ interface StaticValidator
|
|||
|
||||
public static function keySet(Validatable $rule, Validatable ...$rules): ChainedValidator;
|
||||
|
||||
/** @param callable(mixed): Validatable $ruleCreator */
|
||||
public static function lazy(callable $ruleCreator): ChainedValidator;
|
||||
|
||||
public static function lazyConsecutive(callable $ruleCreator, callable ...$ruleCreators): ChainedValidator;
|
||||
|
||||
/** @param "alpha-2"|"alpha-3" $set */
|
||||
|
|
44
library/Rules/Lazy.php
Normal file
44
library/Rules/Lazy.php
Normal file
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Respect\Validation\Rules;
|
||||
|
||||
use Respect\Validation\Exceptions\ComponentException;
|
||||
use Respect\Validation\Helpers\CanBindEvaluateRule;
|
||||
use Respect\Validation\Result;
|
||||
use Respect\Validation\Rules\Core\Standard;
|
||||
use Respect\Validation\Validatable;
|
||||
|
||||
use function call_user_func;
|
||||
|
||||
final class Lazy extends Standard
|
||||
{
|
||||
use CanBindEvaluateRule;
|
||||
|
||||
/** @var callable(mixed): Validatable */
|
||||
private $ruleCreator;
|
||||
|
||||
/**
|
||||
* @param callable(mixed): Validatable $ruleCreator
|
||||
*/
|
||||
public function __construct(callable $ruleCreator)
|
||||
{
|
||||
$this->ruleCreator = $ruleCreator;
|
||||
}
|
||||
|
||||
public function evaluate(mixed $input): Result
|
||||
{
|
||||
$rule = call_user_func($this->ruleCreator, $input);
|
||||
if (!$rule instanceof Validatable) {
|
||||
throw new ComponentException('Lazy failed because it could not create the rule');
|
||||
}
|
||||
|
||||
return $this->bindEvaluate($rule, $this, $input);
|
||||
}
|
||||
}
|
104
tests/integration/rules/lazy.phpt
Normal file
104
tests/integration/rules/lazy.phpt
Normal file
|
@ -0,0 +1,104 @@
|
|||
--FILE--
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require 'vendor/autoload.php';
|
||||
|
||||
use Respect\Validation\Validator as v;
|
||||
|
||||
run([
|
||||
'Default' => [v::lazy(static fn() => v::intType()), true],
|
||||
'Negative' => [v::not(v::lazy(static fn() => v::intType())), 2],
|
||||
'With created name, default' => [
|
||||
v::lazy(static fn() => v::intType()->setName('Created'))->setName('Wrapper'),
|
||||
true,
|
||||
],
|
||||
'With wrapper name, default' => [
|
||||
v::lazy(static fn() => v::intType())->setName('Wrapper'),
|
||||
true,
|
||||
],
|
||||
'With created name, negative' => [
|
||||
v::not(v::lazy(static fn() => v::intType()->setName('Created'))->setName('Wrapped'))->setName('Not'),
|
||||
2,
|
||||
],
|
||||
'With wrapper name, negative' => [
|
||||
v::not(v::lazy(static fn() => v::intType())->setName('Wrapped'))->setName('Not'),
|
||||
2,
|
||||
],
|
||||
'With not name, negative' => [
|
||||
v::not(v::lazy(static fn() => v::intType()))->setName('Not'),
|
||||
2,
|
||||
],
|
||||
'With template, default' => [
|
||||
v::lazy(static fn() => v::intType()),
|
||||
true,
|
||||
'Lazy lizards lounging like lords in the local lagoon',
|
||||
],
|
||||
]);
|
||||
?>
|
||||
--EXPECT--
|
||||
Default
|
||||
⎺⎺⎺⎺⎺⎺⎺
|
||||
`true` must be of type integer
|
||||
- `true` must be of type integer
|
||||
[
|
||||
'intType' => '`true` must be of type integer',
|
||||
]
|
||||
|
||||
Negative
|
||||
⎺⎺⎺⎺⎺⎺⎺⎺
|
||||
2 must not be of type integer
|
||||
- 2 must not be of type integer
|
||||
[
|
||||
'intType' => '2 must not be of type integer',
|
||||
]
|
||||
|
||||
With created name, default
|
||||
⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺
|
||||
Created must be of type integer
|
||||
- Created must be of type integer
|
||||
[
|
||||
'Created' => 'Created must be of type integer',
|
||||
]
|
||||
|
||||
With wrapper name, default
|
||||
⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺
|
||||
Wrapper must be of type integer
|
||||
- Wrapper must be of type integer
|
||||
[
|
||||
'Wrapper' => 'Wrapper must be of type integer',
|
||||
]
|
||||
|
||||
With created name, negative
|
||||
⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺
|
||||
Created must not be of type integer
|
||||
- Created must not be of type integer
|
||||
[
|
||||
'Created' => 'Created must not be of type integer',
|
||||
]
|
||||
|
||||
With wrapper name, negative
|
||||
⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺
|
||||
Wrapped must not be of type integer
|
||||
- Wrapped must not be of type integer
|
||||
[
|
||||
'Wrapped' => 'Wrapped must not be of type integer',
|
||||
]
|
||||
|
||||
With not name, negative
|
||||
⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺
|
||||
Not must not be of type integer
|
||||
- Not must not be of type integer
|
||||
[
|
||||
'Not' => 'Not must not be of type integer',
|
||||
]
|
||||
|
||||
With template, default
|
||||
⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺
|
||||
Lazy lizards lounging like lords in the local lagoon
|
||||
- Lazy lizards lounging like lords in the local lagoon
|
||||
[
|
||||
'intType' => 'Lazy lizards lounging like lords in the local lagoon',
|
||||
]
|
||||
|
61
tests/unit/Rules/LazyTest.php
Normal file
61
tests/unit/Rules/LazyTest.php
Normal file
|
@ -0,0 +1,61 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Respect\Validation\Rules;
|
||||
|
||||
use PHPUnit\Framework\Attributes\CoversClass;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\Attributes\Group;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use Respect\Validation\Exceptions\ComponentException;
|
||||
use Respect\Validation\Test\Rules\Stub;
|
||||
use Respect\Validation\Test\TestCase;
|
||||
|
||||
#[Group('rule')]
|
||||
#[CoversClass(Lazy::class)]
|
||||
final class LazyTest extends TestCase
|
||||
{
|
||||
#[Test]
|
||||
public function itShouldThrowAnExceptionWhenRuleCreatorDoesNotReturnValidatable(): void
|
||||
{
|
||||
// @phpstan-ignore-next-line
|
||||
$rule = new Lazy(static fn () => null);
|
||||
|
||||
$this->expectException(ComponentException::class);
|
||||
$this->expectExceptionMessage('Lazy failed because it could not create the rule');
|
||||
|
||||
$rule->evaluate('something');
|
||||
}
|
||||
|
||||
#[Test]
|
||||
#[DataProvider('providerForAnyValues')]
|
||||
public function itShouldInvalidInputWhenCreatedRuleFails(mixed $input): void
|
||||
{
|
||||
self::assertInvalidInput(new Lazy(static fn ($creatorInput) => Stub::fail(1)), $input);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
#[DataProvider('providerForAnyValues')]
|
||||
public function itShouldValidInputWhenCreatedRulePasses(mixed $input): void
|
||||
{
|
||||
self::assertValidInput(new Lazy(static fn ($creatorInput) => Stub::pass(1)), $input);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
#[DataProvider('providerForAnyValues')]
|
||||
public function itShouldReturnTheResultFromTheCreatedRule(mixed $input): void
|
||||
{
|
||||
$expected = Stub::fail(1)->evaluate($input);
|
||||
|
||||
$rule = new Lazy(static fn ($creatorInput) => Stub::fail(1));
|
||||
$actual = $rule->evaluate($input);
|
||||
|
||||
self::assertEquals($expected, $actual);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue