From a3c197f6006303ac2338fc0e4ece1d07f3c17283 Mon Sep 17 00:00:00 2001 From: Henrique Moody Date: Thu, 26 Dec 2024 03:10:17 +0100 Subject: [PATCH] Handle names via the `Named` rule Because of how the validation engine works now [1], there's no reason to keep adding names to each rule. Instead, create a single rule that handles naming rules with a few other accessories. This change is not necessarily simple, but it shrinks the `Rule` interface, and it's more aligned with how the library works right now. Personally, I think this API is much more straightforward than the `setName()` method, as it's way more explicit about which rule we're naming. Because of this change, the behaviour changed slightly, but it's for the best. Because of this change, I managed to remove a lot of code, but unfortunately, it's quite a big-bang commit. It would be too complicated to make it atomic since names are an intrinsic part of the library. [1]: 238f2d506a6bd717f5bded5fb95c395d2b8f4199 --- bin/create-mixin | 7 +- docs/09-list-of-rules-by-category.md | 4 + docs/rules/Attributes.md | 1 + docs/rules/Named.md | 48 +++++++++++ docs/rules/Not.md | 1 + docs/rules/Templated.md | 1 + library/Helpers/CanExtractPropertyValue.php | 23 ----- .../Helpers/DeprecatedValidatableMethods.php | 39 --------- library/Mixins/Builder.php | 2 + library/Mixins/Chain.php | 2 + library/Result.php | 32 +++++-- library/Rule.php | 4 - library/Rules/Attributes.php | 5 +- library/Rules/Circuit.php | 3 +- library/Rules/Core/Binder.php | 31 ------- library/Rules/Core/Composite.php | 27 +----- library/Rules/Core/FilteredNonEmptyArray.php | 12 +-- library/Rules/Core/Nameable.php | 15 ++++ library/Rules/Core/Reducer.php | 19 +++++ library/Rules/Core/Renameable.php | 14 +++ library/Rules/Core/Standard.php | 28 +++--- library/Rules/Core/Wrapper.php | 17 +--- library/Rules/Each.php | 7 +- library/Rules/Key.php | 20 +++-- library/Rules/KeyExists.php | 3 +- library/Rules/KeyOptional.php | 11 +-- library/Rules/KeySet.php | 3 +- library/Rules/Lazy.php | 3 +- library/Rules/Named.php | 37 ++++++++ library/Rules/Property.php | 32 +++++-- library/Rules/PropertyExists.php | 20 +++-- library/Rules/PropertyOptional.php | 14 +-- library/Rules/When.php | 7 +- library/Validator.php | 13 ++- tests/feature/Rules/EachTest.php | 8 +- tests/feature/Rules/KeyTest.php | 8 +- tests/feature/Rules/NamedTest.php | 85 +++++++++++++++++++ tests/feature/Rules/PropertyTest.php | 8 +- tests/library/Rules/Core/ConcreteStandard.php | 21 ----- .../DeprecatedValidatableMethodsTest.php | 51 ----------- tests/unit/Rules/Core/BinderTest.php | 65 -------------- tests/unit/Rules/Core/CompositeTest.php | 40 --------- tests/unit/Rules/Core/StandardTest.php | 31 +++++-- tests/unit/Rules/Core/WrapperTest.php | 14 --- tests/unit/Rules/KeyOptionalTest.php | 24 ------ tests/unit/Rules/KeyTest.php | 24 ------ tests/unit/Rules/PropertyOptionalTest.php | 24 ------ tests/unit/Rules/PropertyTest.php | 24 ------ 48 files changed, 394 insertions(+), 538 deletions(-) create mode 100644 docs/rules/Named.md delete mode 100644 library/Helpers/CanExtractPropertyValue.php delete mode 100644 library/Helpers/DeprecatedValidatableMethods.php delete mode 100644 library/Rules/Core/Binder.php create mode 100644 library/Rules/Core/Nameable.php create mode 100644 library/Rules/Core/Renameable.php create mode 100644 library/Rules/Named.php create mode 100644 tests/feature/Rules/NamedTest.php delete mode 100644 tests/library/Rules/Core/ConcreteStandard.php delete mode 100644 tests/unit/Helpers/DeprecatedValidatableMethodsTest.php delete mode 100644 tests/unit/Rules/Core/BinderTest.php diff --git a/bin/create-mixin b/bin/create-mixin index dda9c1841..6421b5e91 100755 --- a/bin/create-mixin +++ b/bin/create-mixin @@ -172,6 +172,7 @@ function overwriteFile(string $content, string $basename): void 'PropertyOptional', 'Attributes', 'Templated', + 'Named', ]; $mixins = [ @@ -179,10 +180,10 @@ function overwriteFile(string $content, string $basename): void ['Length', 'length', $numberRelatedRules, []], ['Max', 'max', $numberRelatedRules, []], ['Min', 'min', $numberRelatedRules, []], - ['Not', 'not', [], ['Not', 'NotEmpty', 'NotBlank', 'NotEmoji', 'NotUndef', 'NotOptional', 'NullOr', 'UndefOr', 'Optional', 'Attributes', 'Templated']], - ['NullOr', 'nullOr', [], ['Nullable', 'NullOr', 'Optional', 'NotOptional', 'NotUndef', 'UndefOr', 'Templated']], + ['Not', 'not', [], ['Not', 'NotEmpty', 'NotBlank', 'NotEmoji', 'NotUndef', 'NotOptional', 'NullOr', 'UndefOr', 'Optional', 'Attributes', 'Templated', 'Named']], + ['NullOr', 'nullOr', [], ['Nullable', 'NullOr', 'Optional', 'NotOptional', 'NotUndef', 'UndefOr', 'Templated', 'Named']], ['Property', 'property', [], $structureRelatedRules], - ['UndefOr', 'undefOr', [], ['Nullable', 'NullOr', 'NotOptional', 'NotUndef', 'Optional', 'UndefOr', 'Attributes', 'Templated']], + ['UndefOr', 'undefOr', [], ['Nullable', 'NullOr', 'NotOptional', 'NotUndef', 'Optional', 'UndefOr', 'Attributes', 'Templated', 'Named']], ['', null, [], []], ]; diff --git a/docs/09-list-of-rules-by-category.md b/docs/09-list-of-rules-by-category.md index 187997a49..e50673f38 100644 --- a/docs/09-list-of-rules-by-category.md +++ b/docs/09-list-of-rules-by-category.md @@ -73,6 +73,7 @@ ## Core +- [Named](rules/Named.md) - [Not](rules/Not.md) - [Templated](rules/Templated.md) @@ -159,6 +160,7 @@ ## Miscellaneous - [FilterVar](rules/FilterVar.md) +- [Named](rules/Named.md) - [NotBlank](rules/NotBlank.md) - [NotEmpty](rules/NotEmpty.md) - [NotUndef](rules/NotUndef.md) @@ -260,6 +262,7 @@ - [KeyExists](rules/KeyExists.md) - [KeyOptional](rules/KeyOptional.md) - [KeySet](rules/KeySet.md) +- [Named](rules/Named.md) - [Property](rules/Property.md) - [PropertyExists](rules/PropertyExists.md) - [PropertyOptional](rules/PropertyOptional.md) @@ -392,6 +395,7 @@ - [Mimetype](rules/Mimetype.md) - [Min](rules/Min.md) - [Multiple](rules/Multiple.md) +- [Named](rules/Named.md) - [Negative](rules/Negative.md) - [NfeAccessKey](rules/NfeAccessKey.md) - [Nif](rules/Nif.md) diff --git a/docs/rules/Attributes.md b/docs/rules/Attributes.md index 3d06987cb..c5061cb53 100644 --- a/docs/rules/Attributes.md +++ b/docs/rules/Attributes.md @@ -78,6 +78,7 @@ v::attributes()->assert(new Person('', 'not an email', 'not a date', 'not a phon *** See also: +- [Named](Named.md) - [NullOr](NullOr.md) - [ObjectType](ObjectType.md) - [Property](Property.md) diff --git a/docs/rules/Named.md b/docs/rules/Named.md new file mode 100644 index 000000000..f0bb39cec --- /dev/null +++ b/docs/rules/Named.md @@ -0,0 +1,48 @@ +# Named + +- `Named(Rule $rule, string $name)` + +Validates the input with the given rule, and uses the custom name in the error message. + +```php +v::named(v::email(), 'Your email')->assert('not an email'); +// Message: Your email must be a valid email address +``` + +Here's an example of a similar code, but without using the `Named` rule: + +```php +v::email()->assert('not an email'); +// Message: "not an email" must be a valid email address +``` + +The `Named` rule can be also useful when you're using [Attributes](Attributes.md) and want a custom name for a specific property. + +## Templates + +This rule does not have any templates, as it will use the template of the given rule. + +## Template placeholders + +| Placeholder | Description | +|-------------|---------------------------------------| +| `name` | The value that you define as `$name`. | + +## Categorization + +- Core +- Structures +- Miscellaneous + +## Changelog + +| Version | Description | +|--------:|-------------| +| 3.0.0 | Created | + +*** +See also: + +- [Attributes](Attributes.md) +- [Not](Not.md) +- [Templated](Templated.md) diff --git a/docs/rules/Not.md b/docs/rules/Not.md index 28c060ca9..ed5e7df38 100644 --- a/docs/rules/Not.md +++ b/docs/rules/Not.md @@ -41,5 +41,6 @@ Each other validation has custom messages for negated rules. *** See also: +- [Named](Named.md) - [NoneOf](NoneOf.md) - [Templated](Templated.md) diff --git a/docs/rules/Templated.md b/docs/rules/Templated.md index 8d541c50c..54e8f42b3 100644 --- a/docs/rules/Templated.md +++ b/docs/rules/Templated.md @@ -46,4 +46,5 @@ This rule does not have any templates, as you must define the templates yourself See also: - [Attributes](Attributes.md) +- [Named](Named.md) - [Not](Not.md) diff --git a/library/Helpers/CanExtractPropertyValue.php b/library/Helpers/CanExtractPropertyValue.php deleted file mode 100644 index f44ef1ce5..000000000 --- a/library/Helpers/CanExtractPropertyValue.php +++ /dev/null @@ -1,23 +0,0 @@ - - * SPDX-License-Identifier: MIT - */ - -declare(strict_types=1); - -namespace Respect\Validation\Helpers; - -use ReflectionObject; - -trait CanExtractPropertyValue -{ - public function extractPropertyValue(object $input, string $property): mixed - { - $reflectionObject = new ReflectionObject($input); - $reflectionProperty = $reflectionObject->getProperty($property); - - return $reflectionProperty->isInitialized($input) ? $reflectionProperty->getValue($input) : null; - } -} diff --git a/library/Helpers/DeprecatedValidatableMethods.php b/library/Helpers/DeprecatedValidatableMethods.php deleted file mode 100644 index bb7ca11b3..000000000 --- a/library/Helpers/DeprecatedValidatableMethods.php +++ /dev/null @@ -1,39 +0,0 @@ - - * SPDX-License-Identifier: MIT - */ - -declare(strict_types=1); - -namespace Respect\Validation\Helpers; - -use Respect\Validation\Validator; - -trait DeprecatedValidatableMethods -{ - /** - * @deprecated Calling `validate()` directly is deprecated, please use the `Validator::isValid()` class instead. - */ - public function validate(mixed $input): bool - { - return $this->evaluate($input)->isValid; - } - - /** - * @deprecated Calling `assert()` directly is deprecated, please use the `Validator::assert()` instead. - */ - public function assert(mixed $input): void - { - Validator::create($this)->assert($input); - } - - /** - * @deprecated Calling `check()` directly is deprecated, please use the `Validator::assert()` instead. - */ - public function check(mixed $input): void - { - Validator::create($this)->assert($input); - } -} diff --git a/library/Mixins/Builder.php b/library/Mixins/Builder.php index 7e771aa12..013658a52 100644 --- a/library/Mixins/Builder.php +++ b/library/Mixins/Builder.php @@ -237,6 +237,8 @@ public static function min(Rule $rule): Chain; public static function multiple(int $multipleOf): Chain; + public static function named(Rule $rule, string $name): Chain; + public static function negative(): Chain; public static function nfeAccessKey(): Chain; diff --git a/library/Mixins/Chain.php b/library/Mixins/Chain.php index 5abd15c5a..23106297f 100644 --- a/library/Mixins/Chain.php +++ b/library/Mixins/Chain.php @@ -242,6 +242,8 @@ public function min(Rule $rule): Chain; public function multiple(int $multipleOf): Chain; + public function named(Rule $rule, string $name): Chain; + public function negative(): Chain; public function nfeAccessKey(): Chain; diff --git a/library/Result.php b/library/Result.php index 6db130a25..4a78edba7 100644 --- a/library/Result.php +++ b/library/Result.php @@ -9,6 +9,9 @@ namespace Respect\Validation; +use Respect\Validation\Rules\Core\Nameable; +use Respect\Validation\Rules\Core\Renameable; + use function array_filter; use function array_map; use function count; @@ -25,8 +28,6 @@ final class Result public readonly string $id; - public readonly ?string $name; - /** @param array $parameters */ public function __construct( public readonly bool $isValid, @@ -35,13 +36,12 @@ public function __construct( public readonly array $parameters = [], public readonly string $template = Rule::TEMPLATE_STANDARD, public readonly Mode $mode = Mode::DEFAULT, - ?string $name = null, + public readonly ?string $name = null, ?string $id = null, public readonly ?Result $adjacent = null, public readonly bool $unchangeableId = false, Result ...$children, ) { - $this->name = $rule->getName() ?? $name; $this->id = $id ?? lcfirst(substr((string) strrchr($rule::class, '\\'), 1)); $this->children = $children; } @@ -109,6 +109,11 @@ public function withId(string $id): self return $this->clone(id: $id); } + public function withIdFrom(Rule $rule): self + { + return $this->clone(id: lcfirst(substr((string) strrchr($rule::class, '\\'), 1))); + } + public function withUnchangeableId(string $id): self { return $this->clone(id: $id, unchangeableId: true); @@ -128,14 +133,27 @@ public function withChildren(Result ...$children): self return $this->clone(children: $children); } - public function withNameIfMissing(string $name): self + public function withName(string $name): self { return $this->clone( - name: $this->name ?? $name, - children: array_map(static fn (Result $child) => $child->withNameIfMissing($name), $this->children), + name: $this->rule instanceof Renameable ? $name : ($this->name ?? $name), + adjacent: $this->adjacent?->withName($name), + children: array_map( + static fn (Result $child) => $child->withName($child->name ?? $name), + $this->children + ), ); } + public function withNameFrom(Rule $rule): self + { + if ($rule instanceof Nameable && $rule->getName() !== null) { + return $this->withName($rule->getName()); + } + + return $this; + } + public function withInput(mixed $input): self { $currentInput = $this->input; diff --git a/library/Rule.php b/library/Rule.php index e4bbb92da..6aa3887cd 100644 --- a/library/Rule.php +++ b/library/Rule.php @@ -14,8 +14,4 @@ interface Rule public const TEMPLATE_STANDARD = '__standard__'; public function evaluate(mixed $input): Result; - - public function getName(): ?string; - - public function setName(string $name): static; } diff --git a/library/Rules/Attributes.php b/library/Rules/Attributes.php index 09f2f2419..c89d5bf9a 100644 --- a/library/Rules/Attributes.php +++ b/library/Rules/Attributes.php @@ -14,7 +14,6 @@ use ReflectionObject; use Respect\Validation\Result; use Respect\Validation\Rule; -use Respect\Validation\Rules\Core\Binder; use Respect\Validation\Rules\Core\Reducer; use Respect\Validation\Rules\Core\Standard; @@ -23,7 +22,7 @@ final class Attributes extends Standard { public function evaluate(mixed $input): Result { - $objectType = (new Binder($this, new ObjectType()))->evaluate($input); + $objectType = (new ObjectType())->evaluate($input); if (!$objectType->isValid) { return $objectType->withId('attributes'); } @@ -49,6 +48,6 @@ public function evaluate(mixed $input): Result return (new AlwaysValid())->evaluate($input)->withId('attributes'); } - return (new Binder($this, new Reducer(...$rules)))->evaluate($input)->withId('attributes'); + return (new Reducer(...$rules))->evaluate($input)->withId('attributes'); } } diff --git a/library/Rules/Circuit.php b/library/Rules/Circuit.php index 69f1da1b3..11bca0346 100644 --- a/library/Rules/Circuit.php +++ b/library/Rules/Circuit.php @@ -11,7 +11,6 @@ use Attribute; use Respect\Validation\Result; -use Respect\Validation\Rules\Core\Binder; use Respect\Validation\Rules\Core\Composite; #[Attribute(Attribute::TARGET_PROPERTY | Attribute::IS_REPEATABLE)] @@ -20,7 +19,7 @@ final class Circuit extends Composite public function evaluate(mixed $input): Result { foreach ($this->rules as $rule) { - $result = (new Binder($this, $rule))->evaluate($input); + $result = $rule->evaluate($input); if (!$result->isValid) { return $result; } diff --git a/library/Rules/Core/Binder.php b/library/Rules/Core/Binder.php deleted file mode 100644 index 849565987..000000000 --- a/library/Rules/Core/Binder.php +++ /dev/null @@ -1,31 +0,0 @@ - - * SPDX-License-Identifier: MIT - */ - -declare(strict_types=1); - -namespace Respect\Validation\Rules\Core; - -use Respect\Validation\Result; -use Respect\Validation\Rule; - -final class Binder extends Standard -{ - public function __construct( - private readonly Rule $source, - private readonly Rule $bound, - ) { - } - - public function evaluate(mixed $input): Result - { - if ($this->source->getName() !== null && $this->bound->getName() === null) { - $this->bound->setName($this->source->getName()); - } - - return $this->bound->evaluate($input); - } -} diff --git a/library/Rules/Core/Composite.php b/library/Rules/Core/Composite.php index 6097b35b7..cd83b180e 100644 --- a/library/Rules/Core/Composite.php +++ b/library/Rules/Core/Composite.php @@ -9,20 +9,15 @@ namespace Respect\Validation\Rules\Core; -use Respect\Validation\Helpers\DeprecatedValidatableMethods; use Respect\Validation\Rule; use function array_merge; -abstract class Composite implements Rule +abstract class Composite extends Standard { - use DeprecatedValidatableMethods; - /** @var non-empty-array */ protected readonly array $rules; - private ?string $name = null; - public function __construct(Rule $rule1, Rule $rule2, Rule ...$rules) { $this->rules = array_merge([$rule1, $rule2], $rules); @@ -33,24 +28,4 @@ public function getRules(): array { return $this->rules; } - - public function setName(string $name): static - { - foreach ($this->rules as $rule) { - if ($rule->getName() && $this->name !== $rule->getName()) { - continue; - } - - $rule->setName($name); - } - - $this->name = $name; - - return $this; - } - - public function getName(): ?string - { - return $this->name; - } } diff --git a/library/Rules/Core/FilteredNonEmptyArray.php b/library/Rules/Core/FilteredNonEmptyArray.php index 12f62d62d..6d8147e00 100644 --- a/library/Rules/Core/FilteredNonEmptyArray.php +++ b/library/Rules/Core/FilteredNonEmptyArray.php @@ -15,9 +15,6 @@ use function is_array; use function iterator_to_array; -use function lcfirst; -use function strrchr; -use function substr; abstract class FilteredNonEmptyArray extends Wrapper { @@ -26,16 +23,15 @@ abstract protected function evaluateNonEmptyArray(array $input): Result; public function evaluate(mixed $input): Result { - $id = $this->rule->getName() ?? $this->getName() ?? lcfirst(substr((string) strrchr(static::class, '\\'), 1)); - $iterableResult = (new Binder($this, new IterableType()))->evaluate($input); + $iterableResult = (new IterableType())->evaluate($input); if (!$iterableResult->isValid) { - return $iterableResult->withId($id); + return $iterableResult->withIdFrom($this)->withNameFrom($this->rule); } $array = $this->toArray($input); - $notEmptyResult = (new Binder($this, new NotEmpty()))->evaluate($array); + $notEmptyResult = (new NotEmpty())->evaluate($array); if (!$notEmptyResult->isValid) { - return $notEmptyResult->withId($id); + return $notEmptyResult->withIdFrom($this)->withNameFrom($this->rule); } // @phpstan-ignore-next-line diff --git a/library/Rules/Core/Nameable.php b/library/Rules/Core/Nameable.php new file mode 100644 index 000000000..a751c8e3e --- /dev/null +++ b/library/Rules/Core/Nameable.php @@ -0,0 +1,15 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules\Core; + +interface Nameable +{ + public function getName(): ?string; +} diff --git a/library/Rules/Core/Reducer.php b/library/Rules/Core/Reducer.php index e1a1ef7b5..9889bc116 100644 --- a/library/Rules/Core/Reducer.php +++ b/library/Rules/Core/Reducer.php @@ -11,6 +11,7 @@ use Respect\Validation\Rule; use Respect\Validation\Rules\AllOf; +use Respect\Validation\Rules\Named; use Respect\Validation\Rules\Templated; final class Reducer extends Wrapper @@ -28,4 +29,22 @@ public function withTemplate(?string $template): self return new self(new Templated($this->rule, $template)); } + + public function withName(?string $name): self + { + if ($name === null) { + return $this; + } + + return new self(new Named($this->rule, $name)); + } + + public function getName(): ?string + { + if ($this->rule instanceof Nameable) { + return $this->rule->getName(); + } + + return null; + } } diff --git a/library/Rules/Core/Renameable.php b/library/Rules/Core/Renameable.php new file mode 100644 index 000000000..a8c60ac2e --- /dev/null +++ b/library/Rules/Core/Renameable.php @@ -0,0 +1,14 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules\Core; + +interface Renameable +{ +} diff --git a/library/Rules/Core/Standard.php b/library/Rules/Core/Standard.php index 8b0c6cb43..10f7360da 100644 --- a/library/Rules/Core/Standard.php +++ b/library/Rules/Core/Standard.php @@ -9,24 +9,32 @@ namespace Respect\Validation\Rules\Core; -use Respect\Validation\Helpers\DeprecatedValidatableMethods; use Respect\Validation\Rule; +use Respect\Validation\Validator; abstract class Standard implements Rule { - use DeprecatedValidatableMethods; - - private ?string $name = null; - - public function getName(): ?string + /** + * @deprecated Calling `validate()` directly is deprecated, please use the `Validator::isValid()` class instead. + */ + public function validate(mixed $input): bool { - return $this->name; + return $this->evaluate($input)->isValid; } - public function setName(string $name): static + /** + * @deprecated Calling `assert()` directly is deprecated, please use the `Validator::assert()` instead. + */ + public function assert(mixed $input): void { - $this->name = $name; + Validator::create($this)->assert($input); + } - return $this; + /** + * @deprecated Calling `check()` directly is deprecated, please use the `Validator::assert()` instead. + */ + public function check(mixed $input): void + { + Validator::create($this)->assert($input); } } diff --git a/library/Rules/Core/Wrapper.php b/library/Rules/Core/Wrapper.php index bacea25d5..23232af80 100644 --- a/library/Rules/Core/Wrapper.php +++ b/library/Rules/Core/Wrapper.php @@ -9,14 +9,11 @@ namespace Respect\Validation\Rules\Core; -use Respect\Validation\Helpers\DeprecatedValidatableMethods; use Respect\Validation\Result; use Respect\Validation\Rule; -abstract class Wrapper implements Rule +abstract class Wrapper extends Standard { - use DeprecatedValidatableMethods; - public function __construct( protected readonly Rule $rule ) { @@ -27,18 +24,6 @@ public function evaluate(mixed $input): Result return $this->rule->evaluate($input); } - public function getName(): ?string - { - return $this->rule->getName(); - } - - public function setName(string $name): static - { - $this->rule->setName($name); - - return $this; - } - public function getRule(): Rule { return $this->rule; diff --git a/library/Rules/Each.php b/library/Rules/Each.php index 66f4b8108..ee1dad562 100644 --- a/library/Rules/Each.php +++ b/library/Rules/Each.php @@ -31,10 +31,9 @@ protected function evaluateNonEmptyArray(array $input): Result $children[] = $this->rule->evaluate($value)->withUnchangeableId((string) $key); } $isValid = array_reduce($children, static fn ($carry, $childResult) => $carry && $childResult->isValid, true); - if ($isValid) { - return Result::passed($input, $this)->withChildren(...$children); - } - return Result::failed($input, $this)->withChildren(...$children); + return (new Result($isValid, $input, $this)) + ->withChildren(...$children) + ->withNameFrom($this->rule); } } diff --git a/library/Rules/Key.php b/library/Rules/Key.php index 81b48e267..bebe073c4 100644 --- a/library/Rules/Key.php +++ b/library/Rules/Key.php @@ -12,8 +12,8 @@ use Attribute; use Respect\Validation\Result; use Respect\Validation\Rule; -use Respect\Validation\Rules\Core\Binder; use Respect\Validation\Rules\Core\KeyRelated; +use Respect\Validation\Rules\Core\Nameable; use Respect\Validation\Rules\Core\Wrapper; #[Attribute(Attribute::TARGET_PROPERTY | Attribute::IS_REPEATABLE)] @@ -23,7 +23,6 @@ public function __construct( private readonly int|string $key, Rule $rule, ) { - $rule->setName($rule->getName() ?? (string) $key); parent::__construct($rule); } @@ -34,14 +33,23 @@ public function getKey(): int|string public function evaluate(mixed $input): Result { - $keyExistsResult = (new Binder($this, new KeyExists($this->key)))->evaluate($input); + $keyExistsResult = (new KeyExists($this->key))->evaluate($input); if (!$keyExistsResult->isValid) { - return $keyExistsResult; + return $keyExistsResult->withName($this->getName()); } return $this->rule ->evaluate($input[$this->key]) - ->withUnchangeableId((string) $this->key) - ->withNameIfMissing($this->rule->getName() ?? (string) $this->key); + ->withName($this->getName()) + ->withUnchangeableId((string) $this->key); + } + + private function getName(): string + { + if ($this->rule instanceof Nameable) { + return $this->rule->getName() ?? ((string) $this->key); + } + + return (string) $this->key; } } diff --git a/library/Rules/KeyExists.php b/library/Rules/KeyExists.php index b9b2fda56..3d79e3876 100644 --- a/library/Rules/KeyExists.php +++ b/library/Rules/KeyExists.php @@ -14,6 +14,7 @@ use Respect\Validation\Message\Template; use Respect\Validation\Result; use Respect\Validation\Rules\Core\KeyRelated; +use Respect\Validation\Rules\Core\Renameable; use Respect\Validation\Rules\Core\Standard; use function array_key_exists; @@ -24,7 +25,7 @@ '{{name}} must be present', '{{name}} must not be present', )] -final class KeyExists extends Standard implements KeyRelated +final class KeyExists extends Standard implements KeyRelated, Renameable { public function __construct( private readonly int|string $key diff --git a/library/Rules/KeyOptional.php b/library/Rules/KeyOptional.php index 36d030749..217777b64 100644 --- a/library/Rules/KeyOptional.php +++ b/library/Rules/KeyOptional.php @@ -12,7 +12,6 @@ use Attribute; use Respect\Validation\Result; use Respect\Validation\Rule; -use Respect\Validation\Rules\Core\Binder; use Respect\Validation\Rules\Core\KeyRelated; use Respect\Validation\Rules\Core\Wrapper; @@ -23,7 +22,6 @@ public function __construct( private readonly int|string $key, Rule $rule, ) { - $rule->setName($rule->getName() ?? (string) $key); parent::__construct($rule); } @@ -34,14 +32,11 @@ public function getKey(): int|string public function evaluate(mixed $input): Result { - $keyExistsResult = (new Binder($this, new KeyExists($this->key)))->evaluate($input); + $keyExistsResult = (new KeyExists($this->key))->evaluate($input); if (!$keyExistsResult->isValid) { - return $keyExistsResult->withInvertedMode(); + return $keyExistsResult->withNameFrom($this->rule)->withInvertedMode(); } - return $this->rule - ->evaluate($input[$this->key]) - ->withUnchangeableId((string) $this->key) - ->withNameIfMissing($this->rule->getName() ?? (string) $this->key); + return (new Key($this->key, $this->rule))->evaluate($input); } } diff --git a/library/Rules/KeySet.php b/library/Rules/KeySet.php index 00c9c69c7..a7d4a59af 100644 --- a/library/Rules/KeySet.php +++ b/library/Rules/KeySet.php @@ -14,7 +14,6 @@ use Respect\Validation\Message\Template; use Respect\Validation\Result; use Respect\Validation\Rule; -use Respect\Validation\Rules\Core\Binder; use Respect\Validation\Rules\Core\KeyRelated; use Respect\Validation\Rules\Core\Reducer; use Respect\Validation\Rules\Core\Standard; @@ -76,7 +75,7 @@ public function __construct(Rule $rule, Rule ...$rules) public function evaluate(mixed $input): Result { - $arrayResult = (new Binder($this, new ArrayType()))->evaluate($input); + $arrayResult = (new ArrayType())->evaluate($input); if (!$arrayResult->isValid) { return $arrayResult; } diff --git a/library/Rules/Lazy.php b/library/Rules/Lazy.php index 16e5c980a..3f6527263 100644 --- a/library/Rules/Lazy.php +++ b/library/Rules/Lazy.php @@ -13,7 +13,6 @@ use Respect\Validation\Exceptions\ComponentException; use Respect\Validation\Result; use Respect\Validation\Rule; -use Respect\Validation\Rules\Core\Binder; use Respect\Validation\Rules\Core\Standard; use function call_user_func; @@ -37,6 +36,6 @@ public function evaluate(mixed $input): Result throw new ComponentException('Lazy failed because it could not create the rule'); } - return (new Binder($this, $rule))->evaluate($input); + return $rule->evaluate($input); } } diff --git a/library/Rules/Named.php b/library/Rules/Named.php new file mode 100644 index 000000000..51bd75977 --- /dev/null +++ b/library/Rules/Named.php @@ -0,0 +1,37 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use Attribute; +use Respect\Validation\Result; +use Respect\Validation\Rule; +use Respect\Validation\Rules\Core\Nameable; +use Respect\Validation\Rules\Core\Wrapper; + +#[Attribute(Attribute::TARGET_PROPERTY | Attribute::IS_REPEATABLE)] +final class Named extends Wrapper implements Nameable +{ + public function __construct( + Rule $rule, + private readonly string $name + ) { + parent::__construct($rule); + } + + public function getName(): string + { + return $this->name; + } + + public function evaluate(mixed $input): Result + { + return $this->rule->evaluate($input)->withName($this->name); + } +} diff --git a/library/Rules/Property.php b/library/Rules/Property.php index ea057540c..0eaac24b4 100644 --- a/library/Rules/Property.php +++ b/library/Rules/Property.php @@ -10,35 +10,49 @@ namespace Respect\Validation\Rules; use Attribute; -use Respect\Validation\Helpers\CanExtractPropertyValue; +use ReflectionObject; use Respect\Validation\Result; use Respect\Validation\Rule; -use Respect\Validation\Rules\Core\Binder; +use Respect\Validation\Rules\Core\Nameable; use Respect\Validation\Rules\Core\Wrapper; #[Attribute(Attribute::TARGET_PROPERTY | Attribute::IS_REPEATABLE)] final class Property extends Wrapper { - use CanExtractPropertyValue; - public function __construct( private readonly string $propertyName, Rule $rule, ) { - $rule->setName($rule->getName() ?? $propertyName); parent::__construct($rule); } public function evaluate(mixed $input): Result { - $propertyExistsResult = (new Binder($this, new PropertyExists($this->propertyName)))->evaluate($input); + $propertyExistsResult = (new PropertyExists($this->propertyName))->evaluate($input); if (!$propertyExistsResult->isValid) { - return $propertyExistsResult; + return $propertyExistsResult->withName($this->getName()); } return $this->rule ->evaluate($this->extractPropertyValue($input, $this->propertyName)) - ->withUnchangeableId($this->propertyName) - ->withNameIfMissing($this->rule->getName() ?? $this->propertyName); + ->withName($this->getName()) + ->withUnchangeableId($this->propertyName); + } + + private function getName(): string + { + if ($this->rule instanceof Nameable) { + return $this->rule->getName() ?? $this->propertyName; + } + + return $this->propertyName; + } + + private function extractPropertyValue(object $input, string $property): mixed + { + $reflectionObject = new ReflectionObject($input); + $reflectionProperty = $reflectionObject->getProperty($property); + + return $reflectionProperty->isInitialized($input) ? $reflectionProperty->getValue($input) : null; } } diff --git a/library/Rules/PropertyExists.php b/library/Rules/PropertyExists.php index 438b8a20f..3a8a66c61 100644 --- a/library/Rules/PropertyExists.php +++ b/library/Rules/PropertyExists.php @@ -13,6 +13,7 @@ use ReflectionObject; use Respect\Validation\Message\Template; use Respect\Validation\Result; +use Respect\Validation\Rules\Core\Renameable; use Respect\Validation\Rules\Core\Standard; use function is_object; @@ -22,7 +23,7 @@ '{{name}} must be present', '{{name}} must not be present', )] -final class PropertyExists extends Standard +final class PropertyExists extends Standard implements Renameable { public function __construct( private readonly string $propertyName @@ -31,18 +32,21 @@ public function __construct( public function evaluate(mixed $input): Result { - if (!is_object($input)) { - return Result::failed($input, $this)->withNameIfMissing($this->propertyName)->withId($this->propertyName); - } - - $reflection = new ReflectionObject($input); - return new Result( - $reflection->hasProperty($this->propertyName), + $this->hasProperty($input), $input, $this, name: $this->propertyName, id: $this->propertyName ); } + + private function hasProperty(mixed $input): bool + { + if (!is_object($input)) { + return false; + } + + return (new ReflectionObject($input))->hasProperty($this->propertyName); + } } diff --git a/library/Rules/PropertyOptional.php b/library/Rules/PropertyOptional.php index 88cd90eda..2d33043c5 100644 --- a/library/Rules/PropertyOptional.php +++ b/library/Rules/PropertyOptional.php @@ -10,35 +10,27 @@ namespace Respect\Validation\Rules; use Attribute; -use Respect\Validation\Helpers\CanExtractPropertyValue; use Respect\Validation\Result; use Respect\Validation\Rule; -use Respect\Validation\Rules\Core\Binder; use Respect\Validation\Rules\Core\Wrapper; #[Attribute(Attribute::TARGET_PROPERTY | Attribute::IS_REPEATABLE)] final class PropertyOptional extends Wrapper { - use CanExtractPropertyValue; - public function __construct( private readonly string $propertyName, Rule $rule, ) { - $rule->setName($rule->getName() ?? $propertyName); parent::__construct($rule); } public function evaluate(mixed $input): Result { - $propertyExistsResult = (new Binder($this, new PropertyExists($this->propertyName)))->evaluate($input); + $propertyExistsResult = (new PropertyExists($this->propertyName))->evaluate($input); if (!$propertyExistsResult->isValid) { - return $propertyExistsResult->withInvertedMode(); + return $propertyExistsResult->withNameFrom($this->rule)->withInvertedMode(); } - return $this->rule - ->evaluate($this->extractPropertyValue($input, $this->propertyName)) - ->withUnchangeableId($this->propertyName) - ->withNameIfMissing($this->rule->getName() ?? $this->propertyName); + return (new Property($this->propertyName, $this->rule))->evaluate($input); } } diff --git a/library/Rules/When.php b/library/Rules/When.php index 271d1fd6c..3bd0bb7ec 100644 --- a/library/Rules/When.php +++ b/library/Rules/When.php @@ -12,7 +12,6 @@ use Attribute; use Respect\Validation\Result; use Respect\Validation\Rule; -use Respect\Validation\Rules\Core\Binder; use Respect\Validation\Rules\Core\Standard; #[Attribute(Attribute::TARGET_PROPERTY | Attribute::IS_REPEATABLE)] @@ -34,11 +33,11 @@ public function __construct( public function evaluate(mixed $input): Result { - $whenResult = (new Binder($this, $this->when))->evaluate($input); + $whenResult = $this->when->evaluate($input); if ($whenResult->isValid) { - return (new Binder($this, $this->then))->evaluate($input); + return $this->then->evaluate($input); } - return (new Binder($this, $this->else))->evaluate($input); + return $this->else->evaluate($input); } } diff --git a/library/Validator.php b/library/Validator.php index dfd380495..f254ba03d 100644 --- a/library/Validator.php +++ b/library/Validator.php @@ -13,7 +13,7 @@ use Respect\Validation\Message\Formatter; use Respect\Validation\Message\Translator; use Respect\Validation\Mixins\Builder; -use Respect\Validation\Rules\Core\Binder; +use Respect\Validation\Rules\Core\Nameable; use Respect\Validation\Rules\Core\Reducer; use Throwable; @@ -24,7 +24,7 @@ /** * @mixin Builder */ -final class Validator implements Rule +final class Validator implements Rule, Nameable { /** @var array */ private array $rules = []; @@ -60,7 +60,7 @@ public static function create(Rule ...$rules): self public function evaluate(mixed $input): Result { - return (new Binder($this, (new Reducer(...$this->rules))->withTemplate($this->template)))->evaluate($input); + return $this->getRule()->evaluate($input); } public function isValid(mixed $input): bool @@ -118,6 +118,11 @@ public function addRule(Rule $rule): self return $this; } + public function getRule(): Reducer + { + return (new Reducer(...$this->rules))->withTemplate($this->template)->withName($this->name); + } + /** @return array */ public function getRules(): array { @@ -142,7 +147,7 @@ public function check(mixed $input): void public function getName(): ?string { - return $this->name; + return $this->getRule()->getName() ?? $this->name; } public function setName(string $name): static diff --git a/tests/feature/Rules/EachTest.php b/tests/feature/Rules/EachTest.php index a61103507..4939ad749 100644 --- a/tests/feature/Rules/EachTest.php +++ b/tests/feature/Rules/EachTest.php @@ -56,17 +56,17 @@ )); test('With name, non-iterable', expectAll( - fn() => v::each(v::intType()->setName('Wrapped'))->setName('Wrapper')->assert(null), + fn() => v::each(v::intType()->setName('Wrapped'))->assert(null), 'Wrapped must be iterable', '- Wrapped must be iterable', - ['Wrapped' => 'Wrapped must be iterable'], + ['each' => 'Wrapped must be iterable'], )); test('With name, empty', expectAll( - fn() => v::each(v::intType()->setName('Wrapped'))->setName('Wrapper')->assert([]), + fn() => v::each(v::intType()->setName('Wrapped'))->assert([]), 'Wrapped must not be empty', '- Wrapped must not be empty', - ['Wrapped' => 'Wrapped must not be empty'], + ['each' => 'Wrapped must not be empty'], )); test('With name, default', expectAll( diff --git a/tests/feature/Rules/KeyTest.php b/tests/feature/Rules/KeyTest.php index 34eebf79c..9d3205063 100644 --- a/tests/feature/Rules/KeyTest.php +++ b/tests/feature/Rules/KeyTest.php @@ -36,7 +36,7 @@ )); test('With wrapped name, missing key', expectAll( - fn() => v::key('foo', v::intType()->setName('Wrapped'))->setName('Wrapper')->assert([]), + fn() => v::key('foo', v::intType()->setName('Wrapped'))->assert([]), 'Wrapped must be present', '- Wrapped must be present', ['foo' => 'Wrapped must be present'], @@ -65,9 +65,9 @@ test('With wrapper name, missing key', expectAll( fn() => v::key('foo', v::intType())->setName('Wrapper')->assert([]), - 'foo must be present', - '- foo must be present', - ['foo' => 'foo must be present'], + 'Wrapper must be present', + '- Wrapper must be present', + ['foo' => 'Wrapper must be present'], )); test('With wrapper name, inverted', expectAll( diff --git a/tests/feature/Rules/NamedTest.php b/tests/feature/Rules/NamedTest.php new file mode 100644 index 000000000..4f31fb85e --- /dev/null +++ b/tests/feature/Rules/NamedTest.php @@ -0,0 +1,85 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +test('Default', expectAll( + fn() => v::named(v::stringType(), 'Potato')->assert(12), + 'Potato must be a string', + '- Potato must be a string', + ['stringType' => 'Potato must be a string'], +)); + +test('Inverted', expectAll( + fn() => v::not(v::named(v::intType(), 'Zucchini'))->assert(12), + 'Zucchini must not be an integer', + '- Zucchini must not be an integer', + ['notIntType' => 'Zucchini must not be an integer'], +)); + +test('Template in Validator', expectAll( + fn() => v::named(v::stringType(), 'Eggplant') + ->setName('Mushroom') + ->assert(12), + 'Eggplant must be a string', + '- Eggplant must be a string', + ['stringType' => 'Eggplant must be a string'], +)); + +test('With bound', expectAll( + fn() => v::named(v::attributes(), 'Pumpkin')->assert(null), + 'Pumpkin must be an object', + '- Pumpkin must be an object', + ['attributes' => 'Pumpkin must be an object'], +)); + +test('With key that does not exist', expectAll( + fn() => v::key('vegetable', v::named(v::stringType(), 'Paprika'))->assert([]), + 'Paprika must be present', + '- Paprika must be present', + ['vegetable' => 'Paprika must be present'], +)); + +test('With property that does not exist', expectAll( + fn() => v::key('vegetable', v::named(v::stringType(), 'Broccoli'))->assert((object) []), + 'Broccoli must be present', + '- Broccoli must be present', + ['vegetable' => 'Broccoli must be present'], +)); + +test('With key that fails validation', expectAll( + fn() => v::key('vegetable', v::named(v::stringType(), 'Artichoke'))->assert(['vegetable' => 12]), + 'Artichoke must be a string', + '- Artichoke must be a string', + ['vegetable' => 'Artichoke must be a string'], +)); + +test('With nested key that fails validation', expectAll( + fn() => v::key( + 'vegetables', + v::named( + v::create() + ->key('root', v::stringType()) + ->key('stems', v::stringType()) + ->keyExists('fruits'), + 'Vegetables' + ), + )->assert(['vegetables' => ['root' => 12, 'stems' => 12]]), + 'root must be a string', + <<<'FULL_MESSAGE' + - All the required rules must pass for Vegetables + - root must be a string + - stems must be a string + - fruits must be present + FULL_MESSAGE, + [ + '__root__' => 'All the required rules must pass for Vegetables', + 'root' => 'root must be a string', + 'stems' => 'stems must be a string', + 'fruits' => 'fruits must be present', + ], +)); diff --git a/tests/feature/Rules/PropertyTest.php b/tests/feature/Rules/PropertyTest.php index 49b693c0c..52ecb7634 100644 --- a/tests/feature/Rules/PropertyTest.php +++ b/tests/feature/Rules/PropertyTest.php @@ -36,7 +36,7 @@ )); test('With wrapped name, missing property', expectAll( - fn() => v::property('foo', v::intType()->setName('Wrapped'))->setName('Wrapper')->assert(new stdClass()), + fn() => v::property('foo', v::intType()->setName('Wrapped'))->assert(new stdClass()), 'Wrapped must be present', '- Wrapped must be present', ['foo' => 'Wrapped must be present'], @@ -69,9 +69,9 @@ test('With wrapper name, missing property', expectAll( fn() => v::property('foo', v::intType())->setName('Wrapper')->assert(new stdClass()), - 'foo must be present', - '- foo must be present', - ['foo' => 'foo must be present'], + 'Wrapper must be present', + '- Wrapper must be present', + ['foo' => 'Wrapper must be present'], )); test('With wrapper name, inverted', expectAll( diff --git a/tests/library/Rules/Core/ConcreteStandard.php b/tests/library/Rules/Core/ConcreteStandard.php deleted file mode 100644 index 7d6219536..000000000 --- a/tests/library/Rules/Core/ConcreteStandard.php +++ /dev/null @@ -1,21 +0,0 @@ - - * SPDX-License-Identifier: MIT - */ - -declare(strict_types=1); - -namespace Respect\Validation\Test\Rules\Core; - -use Respect\Validation\Result; -use Respect\Validation\Rules\Core\Standard; - -final class ConcreteStandard extends Standard -{ - public function evaluate(mixed $input): Result - { - return Result::passed($input, $this); - } -} diff --git a/tests/unit/Helpers/DeprecatedValidatableMethodsTest.php b/tests/unit/Helpers/DeprecatedValidatableMethodsTest.php deleted file mode 100644 index ced66518a..000000000 --- a/tests/unit/Helpers/DeprecatedValidatableMethodsTest.php +++ /dev/null @@ -1,51 +0,0 @@ - - * SPDX-License-Identifier: MIT - */ - -declare(strict_types=1); - -namespace Respect\Validation\Helpers; - -use PHPUnit\Framework\Attributes\CoversClass; -use PHPUnit\Framework\Attributes\Test; -use Respect\Validation\Exceptions\ValidationException; -use Respect\Validation\Test\Rules\Stub; -use Respect\Validation\Test\TestCase; - -#[CoversClass(DeprecatedValidatableMethods::class)] -final class DeprecatedValidatableMethodsTest extends TestCase -{ - #[Test] - public function itShouldAllowUsingTheValidateMethod(): void - { - $rule = Stub::pass(1); - - // @phpstan-ignore-next-line - self::assertTrue($rule->validate('any')); - } - - #[Test] - public function itShouldAllowUsingTheAssertMethod(): void - { - $rule = Stub::fail(1); - - self::expectException(ValidationException::class); - - // @phpstan-ignore-next-line - $rule->assert('any'); - } - - #[Test] - public function itShouldAllowUsingTheCheckMethod(): void - { - $rule = Stub::fail(1); - - self::expectException(ValidationException::class); - - // @phpstan-ignore-next-line - $rule->check('any'); - } -} diff --git a/tests/unit/Rules/Core/BinderTest.php b/tests/unit/Rules/Core/BinderTest.php deleted file mode 100644 index 25553c1a0..000000000 --- a/tests/unit/Rules/Core/BinderTest.php +++ /dev/null @@ -1,65 +0,0 @@ - - * SPDX-License-Identifier: MIT - */ - -declare(strict_types=1); - -namespace Respect\Validation\Rules\Core; - -use PHPUnit\Framework\Attributes\CoversClass; -use PHPUnit\Framework\Attributes\Test; -use PHPUnit\Framework\TestCase; -use Respect\Validation\Rules\AlwaysInvalid; - -#[CoversClass(Binder::class)] -final class BinderTest extends TestCase -{ - #[Test] - public function shouldBindNameToBoundRule(): void - { - $sourceName = 'source name'; - - $source = new AlwaysInvalid(); - $source->setName($sourceName); - - $bound = new AlwaysInvalid(); - $binder = new Binder($source, $bound); - $result = $binder->evaluate(null); - - self::assertSame($sourceName, $bound->getName()); - self::assertSame($sourceName, $result->name); - } - - #[Test] - public function shouldNotBindNameToBoundRuleWhenItAlreadyHasSomeName(): void - { - $source = new AlwaysInvalid(); - $source->setName('source name'); - - $boundName = 'bound name'; - - $bound = new AlwaysInvalid(); - $bound->setName($boundName); - - $binder = new Binder($source, $bound); - $result = $binder->evaluate(null); - - self::assertSame($boundName, $bound->getName()); - self::assertSame($boundName, $result->name); - } - - #[Test] - public function shouldNotBindNameToBoundRuleWhenSourceHasNoName(): void - { - $bound = new AlwaysInvalid(); - - $binder = new Binder(new AlwaysInvalid(), $bound); - $result = $binder->evaluate(null); - - self::assertNull($bound->getName()); - self::assertNull($result->name); - } -} diff --git a/tests/unit/Rules/Core/CompositeTest.php b/tests/unit/Rules/Core/CompositeTest.php index 59638b0c4..fa3cc5d95 100644 --- a/tests/unit/Rules/Core/CompositeTest.php +++ b/tests/unit/Rules/Core/CompositeTest.php @@ -30,44 +30,4 @@ public function itShouldReturnItsChildren(): void self::assertCount(3, $actual); self::assertEquals($expected, $actual); } - - #[Test] - public function itShouldUpdateTheNameOfTheChildWhenUpdatingItsName(): void - { - $ruleName = 'something'; - - $rule1 = Stub::daze(); - $rule2 = Stub::daze(); - - $composite = new ConcreteComposite($rule1, $rule2); - - self::assertNull($rule1->getName()); - self::assertNull($rule2->getName()); - - $composite->setName($ruleName); - - self::assertEquals($ruleName, $rule1->getName()); - self::assertEquals($ruleName, $rule2->getName()); - self::assertEquals($ruleName, $composite->getName()); - } - - #[Test] - public function itShouldNotUpdateTheNameOfTheChildWhenUpdatingItsNameIfTheChildAlreadyHasSomeName(): void - { - $ruleName1 = 'something'; - $ruleName2 = 'something else'; - - $rule1 = Stub::daze(); - $rule1->setName($ruleName1); - - $rule2 = Stub::daze(); - $rule2->setName($ruleName1); - - $composite = new ConcreteComposite($rule1, $rule2); - $composite->setName($ruleName2); - - self::assertEquals($ruleName1, $rule1->getName()); - self::assertEquals($ruleName1, $rule2->getName()); - self::assertEquals($ruleName2, $composite->getName()); - } } diff --git a/tests/unit/Rules/Core/StandardTest.php b/tests/unit/Rules/Core/StandardTest.php index c81e0cdcd..b546bac13 100644 --- a/tests/unit/Rules/Core/StandardTest.php +++ b/tests/unit/Rules/Core/StandardTest.php @@ -12,7 +12,8 @@ use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\Attributes\Test; -use Respect\Validation\Test\Rules\Core\ConcreteStandard; +use Respect\Validation\Exceptions\ValidationException; +use Respect\Validation\Test\Rules\Stub; use Respect\Validation\Test\TestCase; #[Group('core')] @@ -20,19 +21,33 @@ final class StandardTest extends TestCase { #[Test] - public function itShouldNotHaveAnyNameByDefault(): void + public function itShouldAllowUsingTheValidateMethod(): void { - $rule = new ConcreteStandard(); + $rule = Stub::pass(1); - self::assertNull($rule->getName()); + // @phpstan-ignore-next-line + self::assertTrue($rule->validate('any')); } #[Test] - public function itShouldBeAbleToSetName(): void + public function itShouldAllowUsingTheAssertMethod(): void { - $rule = new ConcreteStandard(); - $rule->setName('foo'); + $rule = Stub::fail(1); - self::assertEquals('foo', $rule->getName()); + self::expectException(ValidationException::class); + + // @phpstan-ignore-next-line + $rule->assert('any'); + } + + #[Test] + public function itShouldAllowUsingTheCheckMethod(): void + { + $rule = Stub::fail(1); + + self::expectException(ValidationException::class); + + // @phpstan-ignore-next-line + $rule->check('any'); } } diff --git a/tests/unit/Rules/Core/WrapperTest.php b/tests/unit/Rules/Core/WrapperTest.php index 8c410ce6a..5b8969a8f 100644 --- a/tests/unit/Rules/Core/WrapperTest.php +++ b/tests/unit/Rules/Core/WrapperTest.php @@ -31,18 +31,4 @@ public function shouldUseWrappedToEvaluate(): void self::assertEquals($wrapped->evaluate($input), $wrapper->evaluate($input)); } - - #[Test] - public function shouldPassNameOnToWrapped(): void - { - $name = 'Whatever'; - - $rule = Stub::pass(1); - - $sut = new ConcreteWrapper($rule); - $sut->setName($name); - - self::assertSame($name, $rule->getName()); - self::assertSame($name, $sut->getName()); - } } diff --git a/tests/unit/Rules/KeyOptionalTest.php b/tests/unit/Rules/KeyOptionalTest.php index f08229697..ab20b1b49 100644 --- a/tests/unit/Rules/KeyOptionalTest.php +++ b/tests/unit/Rules/KeyOptionalTest.php @@ -51,28 +51,4 @@ public function itShouldValidateExistingKeysWithWrappedRule(int|string $key, arr self::assertEquals($wrapped->inputs, [$input[$key]]); } - - #[Test] - public function itShouldUpdateWrappedNameWithTheGivenKeyWhenItIsString(): void - { - $key = 'toodaloo'; - - $wrapped = Stub::daze(); - - new KeyOptional($key, $wrapped); - - self::assertEquals($key, $wrapped->getName()); - } - - #[Test] - public function itShouldUpdateWrappedNameWithTheGivenKeyWhenItIsInteger(): void - { - $key = 123; - - $wrapped = Stub::daze(); - - new KeyOptional($key, $wrapped); - - self::assertEquals($key, $wrapped->getName()); - } } diff --git a/tests/unit/Rules/KeyTest.php b/tests/unit/Rules/KeyTest.php index fdc3b6304..c3294f8fc 100644 --- a/tests/unit/Rules/KeyTest.php +++ b/tests/unit/Rules/KeyTest.php @@ -60,28 +60,4 @@ public function itShouldReturnDefinedKey(): void self::assertSame($key, $rule->getKey()); } - - #[Test] - public function itShouldUpdateWrappedNameWithTheGivenKeyWhenItIsString(): void - { - $key = 'toodaloo'; - - $wrapped = Stub::daze(); - - new Key($key, $wrapped); - - self::assertEquals($key, $wrapped->getName()); - } - - #[Test] - public function itShouldUpdateWrappedNameWithTheGivenKeyWhenItIsInteger(): void - { - $key = 123; - - $wrapped = Stub::daze(); - - new Key($key, $wrapped); - - self::assertEquals($key, $wrapped->getName()); - } } diff --git a/tests/unit/Rules/PropertyOptionalTest.php b/tests/unit/Rules/PropertyOptionalTest.php index 5551836a2..22cc4c0a1 100644 --- a/tests/unit/Rules/PropertyOptionalTest.php +++ b/tests/unit/Rules/PropertyOptionalTest.php @@ -62,28 +62,4 @@ public function itShouldValidatePropertyWithTheWrappedRule(): void self::assertEquals([$object->foo], $wrapped->inputs); } - - #[Test] - public function itShouldUpdateWrappedRuleNameWithTheGivenName(): void - { - $property = 'foo'; - $wrapped = Stub::daze(); - - new PropertyOptional($property, $wrapped); - - self::assertEquals($property, $wrapped->getName()); - } - - #[Test] - public function itShouldNotUpdateWrappedRuleNameWithTheGivenNameWhenRuleAlreadyHasName(): void - { - $name = 'bar'; - - $wrapped = Stub::daze(); - $wrapped->setName($name); - - new PropertyOptional('foo', $wrapped); - - self::assertEquals($name, $wrapped->getName()); - } } diff --git a/tests/unit/Rules/PropertyTest.php b/tests/unit/Rules/PropertyTest.php index f8a10a38f..aa634fabf 100644 --- a/tests/unit/Rules/PropertyTest.php +++ b/tests/unit/Rules/PropertyTest.php @@ -62,28 +62,4 @@ public function itShouldValidatePropertyWithTheWrappedRule(): void self::assertEquals([$object->foo], $wrapped->inputs); } - - #[Test] - public function itShouldUpdateWrappedRuleNameWithTheGivenName(): void - { - $property = 'foo'; - $wrapped = Stub::daze(); - - new Property($property, $wrapped); - - self::assertEquals($property, $wrapped->getName()); - } - - #[Test] - public function itShouldNotUpdateWrappedRuleNameWithTheGivenNameWhenRuleAlreadyHasName(): void - { - $name = 'bar'; - - $wrapped = Stub::daze(); - $wrapped->setName($name); - - new Property('foo', $wrapped); - - self::assertEquals($name, $wrapped->getName()); - } }