mirror of
https://github.com/Respect/Validation.git
synced 2024-06-08 08:42:15 +02:00
Split the "Property" rule
Currently, the Property rule has a third parameter that allows the validation of the wrapped rule to be optional, meaning that the validation will only happen if the property exists. That parameter makes the rule harder to understand at times. I'm splitting the Property rule into Property, PropertyExists, and PropertyOptional. That way, it becomes apparent when someone wants only to validate whether a property exists or if they will validate the value of the property only when it exists. I deliberately didn't create an abstract class because those rules are different enough not to have an abstraction. In fact, I can see myself deleting the AbstractRelated after I refactor the KeyNested rule. Signed-off-by: Henrique Moody <henriquemoody@gmail.com>
This commit is contained in:
parent
a647a4737b
commit
d36572cc25
|
@ -172,6 +172,7 @@
|
|||
- [OneOf](rules/OneOf.md)
|
||||
- [Optional](rules/Optional.md)
|
||||
- [Property](rules/Property.md)
|
||||
- [PropertyOptional](rules/PropertyOptional.md)
|
||||
- [When](rules/When.md)
|
||||
|
||||
## Numbers
|
||||
|
@ -203,6 +204,8 @@
|
|||
- [Instance](rules/Instance.md)
|
||||
- [ObjectType](rules/ObjectType.md)
|
||||
- [Property](rules/Property.md)
|
||||
- [PropertyExists](rules/PropertyExists.md)
|
||||
- [PropertyOptional](rules/PropertyOptional.md)
|
||||
|
||||
## Strings
|
||||
|
||||
|
@ -249,6 +252,8 @@
|
|||
- [KeyOptional](rules/KeyOptional.md)
|
||||
- [KeySet](rules/KeySet.md)
|
||||
- [Property](rules/Property.md)
|
||||
- [PropertyExists](rules/PropertyExists.md)
|
||||
- [PropertyOptional](rules/PropertyOptional.md)
|
||||
|
||||
## Transformations
|
||||
|
||||
|
@ -405,6 +410,8 @@
|
|||
- [PrimeNumber](rules/PrimeNumber.md)
|
||||
- [Printable](rules/Printable.md)
|
||||
- [Property](rules/Property.md)
|
||||
- [PropertyExists](rules/PropertyExists.md)
|
||||
- [PropertyOptional](rules/PropertyOptional.md)
|
||||
- [PublicDomainSuffix](rules/PublicDomainSuffix.md)
|
||||
- [Punct](rules/Punct.md)
|
||||
- [Readable](rules/Readable.md)
|
||||
|
|
|
@ -50,3 +50,5 @@ See also:
|
|||
- [KeyOptional](KeyOptional.md)
|
||||
- [KeySet](KeySet.md)
|
||||
- [Property](Property.md)
|
||||
- [PropertyExists](PropertyExists.md)
|
||||
- [PropertyOptional](PropertyOptional.md)
|
||||
|
|
|
@ -40,4 +40,5 @@ See also:
|
|||
- [Key](Key.md)
|
||||
- [KeyOptional](KeyOptional.md)
|
||||
- [Property](Property.md)
|
||||
|
||||
- [PropertyExists](PropertyExists.md)
|
||||
- [PropertyOptional](PropertyOptional.md)
|
||||
|
|
|
@ -47,5 +47,7 @@ See also:
|
|||
|
||||
- [Key](Key.md)
|
||||
- [Property](Property.md)
|
||||
- [PropertyExists](PropertyExists.md)
|
||||
- [PropertyOptional](PropertyOptional.md)
|
||||
|
||||
[Yii2 ArrayHelper]: https://github.com/yiisoft/yii2/blob/68c30c1/framework/helpers/BaseArrayHelper.php "Yii2 ArrayHelper"
|
||||
|
|
|
@ -55,6 +55,8 @@ See also:
|
|||
- [Key](Key.md)
|
||||
- [KeyExists](KeyExists.md)
|
||||
- [Property](Property.md)
|
||||
- [PropertyExists](PropertyExists.md)
|
||||
- [PropertyOptional](PropertyOptional.md)
|
||||
|
||||
[array]: https://www.php.net/array
|
||||
[ArrayAccess]: https://www.php.net/arrayaccess
|
||||
|
|
|
@ -33,6 +33,8 @@ See also:
|
|||
- [NullType](NullType.md)
|
||||
- [Number](Number.md)
|
||||
- [Property](Property.md)
|
||||
- [PropertyExists](PropertyExists.md)
|
||||
- [PropertyOptional](PropertyOptional.md)
|
||||
- [ResourceType](ResourceType.md)
|
||||
- [StringType](StringType.md)
|
||||
- [StringVal](StringVal.md)
|
||||
|
|
|
@ -1,32 +1,38 @@
|
|||
# Property
|
||||
|
||||
- `Property(string $name)`
|
||||
- `Property(string $name, Validatable $rule)`
|
||||
- `Property(string $name, Validatable $rule, bool $mandatory)`
|
||||
- `Property(string $propertyName, Validatable $rule)`
|
||||
|
||||
Validates an object property, even private ones.
|
||||
Validates an object property against a given rule.
|
||||
|
||||
```php
|
||||
$obj = new stdClass;
|
||||
$obj->foo = 'bar';
|
||||
$object = new stdClass;
|
||||
$object->name = 'The Respect Panda';
|
||||
$object->email = 'therespectpanda@gmail.com';
|
||||
|
||||
v::property('foo')->validate($obj); // true
|
||||
```
|
||||
v::property('name', v::equals('The Respect Panda'))->validate($object); // true
|
||||
|
||||
You can also validate the property itself:
|
||||
v::property('email', v::email())->validate($object); // true
|
||||
|
||||
```php
|
||||
v::property('foo', v::equals('bar'))->validate($obj); // true
|
||||
```
|
||||
|
||||
Third parameter makes the property presence optional:
|
||||
|
||||
```php
|
||||
v::property('lorem', v::stringType(), false)->validate($obj); // true
|
||||
v::property('email', v::email()->endsWith('@example.com'))->assert($object); // false
|
||||
```
|
||||
|
||||
The name of this validator is automatically set to the property name.
|
||||
|
||||
```php
|
||||
v::property('website', v::url())->assert($object);
|
||||
// message: website must be present
|
||||
|
||||
v::property('name', v::uppercase())->assert($object);
|
||||
// message: name must be uppercase
|
||||
```
|
||||
|
||||
## Note
|
||||
|
||||
This rule will validate public, private, protected, uninitialised, and static properties.
|
||||
|
||||
* To only validate if a property exists, use [PropertyExists](PropertyExists.md) instead.
|
||||
* To validate a property against a given rule only if the property exists, use [PropertyOptional](PropertyOptional.md) instead.
|
||||
|
||||
## Categorization
|
||||
|
||||
- Nesting
|
||||
|
@ -35,10 +41,10 @@ The name of this validator is automatically set to the property name.
|
|||
|
||||
## Changelog
|
||||
|
||||
Version | Description
|
||||
--------|-------------
|
||||
3.0.0 | Renamed from `Attribute` to `Property`
|
||||
0.3.9 | Created
|
||||
| Version | Description |
|
||||
| ------: |--------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| 3.0.0 | Renamed from `Attribute` to `Property`, and split by [PropertyExists](PropertyExists.md) and [PropertyOptional](PropertyOptional.md) |
|
||||
| 0.3.9 | Created |
|
||||
|
||||
***
|
||||
See also:
|
||||
|
@ -48,3 +54,5 @@ See also:
|
|||
- [KeyNested](KeyNested.md)
|
||||
- [KeyOptional](KeyOptional.md)
|
||||
- [ObjectType](ObjectType.md)
|
||||
- [PropertyExists](PropertyExists.md)
|
||||
- [PropertyOptional](PropertyOptional.md)
|
||||
|
|
44
docs/rules/PropertyExists.md
Normal file
44
docs/rules/PropertyExists.md
Normal file
|
@ -0,0 +1,44 @@
|
|||
# PropertyExists
|
||||
|
||||
- `PropertyExists(string $propertyName)`
|
||||
|
||||
Validates if an object property exists.
|
||||
|
||||
```php
|
||||
$object = new stdClass;
|
||||
$object->name = 'The Respect Panda';
|
||||
$object->email = 'therespectpanda@gmail.com';
|
||||
|
||||
v::propertyExists('name')->validate($object); // true
|
||||
v::propertyExists('email')->validate($object); // true
|
||||
v::propertyExists('website')->validate($object); // false
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
This rule will validate public, private, protected, uninitialised, and static properties.
|
||||
|
||||
* To validate a property against a given rule requiring the property to exist, use [Property](Property.md) instead.
|
||||
* To validate a property against a given rule only if the property exists, use [PropertyOptional](PropertyOptional.md) instead.
|
||||
|
||||
## Categorization
|
||||
|
||||
- Objects
|
||||
- Structures
|
||||
|
||||
## Changelog
|
||||
|
||||
| Version | Description |
|
||||
| ------: |--------------------------------------|
|
||||
| 3.0.0 | Created from [Property](Property.md) |
|
||||
|
||||
***
|
||||
See also:
|
||||
|
||||
- [Key](Key.md)
|
||||
- [KeyExists](KeyExists.md)
|
||||
- [KeyNested](KeyNested.md)
|
||||
- [KeyOptional](KeyOptional.md)
|
||||
- [ObjectType](ObjectType.md)
|
||||
- [Property](Property.md)
|
||||
- [PropertyOptional](PropertyOptional.md)
|
63
docs/rules/PropertyOptional.md
Normal file
63
docs/rules/PropertyOptional.md
Normal file
|
@ -0,0 +1,63 @@
|
|||
# PropertyOptional
|
||||
|
||||
- `PropertyOptional(string $propertyName, Validatable $rule)`
|
||||
|
||||
Validates an object property against a given rule only if the property exists.
|
||||
|
||||
```php
|
||||
$object = new stdClass;
|
||||
$object->name = 'The Respect Panda';
|
||||
$object->email = 'therespectpanda@gmail.com';
|
||||
|
||||
v::propertyOptional('name', v::notEmpty())->validate($object); // true
|
||||
v::propertyOptional('email', v::email())->validate($object); // true
|
||||
|
||||
v::propertyOptional('age', v::intVal())->validate($object); // true
|
||||
v::propertyOptional('website', v::url())->validate($object); // true
|
||||
|
||||
v::propertyOptional('name', v::lowercase())->validate($object); // false
|
||||
```
|
||||
|
||||
The name of this validator is automatically set to the property name.
|
||||
|
||||
```php
|
||||
v::propertyOptional('email', v::endsWith('@example.com'))->assert($object);
|
||||
// message: email must end with "@example.com"
|
||||
```
|
||||
|
||||
## Note
|
||||
|
||||
This rule will validate public, private, protected, uninitialised, and static properties. However, it will pass for
|
||||
anything that is not an object because it will always pass when it doesn't find a property in the input. If you want to
|
||||
ensure the input is an object, use [ObjectType](ObjectType.md) with it.
|
||||
|
||||
```php
|
||||
v::propertyOptional('name', v::notEmpty())->validate('Not an object'); // true
|
||||
v::objectType()->propertyOptional('name', v::notEmpty())->validate('Not an object'); // false
|
||||
```
|
||||
|
||||
* To only validate if a property exists, use [PropertyExists](PropertyExists.md) instead.
|
||||
* To validate a property against a given rule requiring the property to exist, use [Property](Property.md) instead.
|
||||
|
||||
## Categorization
|
||||
|
||||
- Nesting
|
||||
- Objects
|
||||
- Structures
|
||||
|
||||
## Changelog
|
||||
|
||||
| Version | Description |
|
||||
| ------: |--------------------------------------|
|
||||
| 3.0.0 | Created from [Property](Property.md) |
|
||||
|
||||
***
|
||||
See also:
|
||||
|
||||
- [Key](Key.md)
|
||||
- [KeyExists](KeyExists.md)
|
||||
- [KeyNested](KeyNested.md)
|
||||
- [KeyOptional](KeyOptional.md)
|
||||
- [ObjectType](ObjectType.md)
|
||||
- [Property](Property.md)
|
||||
- [PropertyExists](PropertyExists.md)
|
23
library/Helpers/CanExtractPropertyValue.php
Normal file
23
library/Helpers/CanExtractPropertyValue.php
Normal file
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
|
||||
* 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;
|
||||
}
|
||||
}
|
|
@ -30,11 +30,11 @@ interface ChainedValidator extends Validatable
|
|||
|
||||
public function arrayVal(): ChainedValidator;
|
||||
|
||||
public function property(
|
||||
string $reference,
|
||||
?Validatable $validator = null,
|
||||
bool $mandatory = true
|
||||
): ChainedValidator;
|
||||
public function property(string $propertyName, Validatable $validator): ChainedValidator;
|
||||
|
||||
public function propertyExists(string $propertyName): ChainedValidator;
|
||||
|
||||
public function propertyOptional(string $propertyName, Validatable $validator): ChainedValidator;
|
||||
|
||||
public function base(int $base, ?string $chars = null): ChainedValidator;
|
||||
|
||||
|
|
|
@ -30,11 +30,11 @@ interface StaticValidator
|
|||
|
||||
public static function arrayVal(): ChainedValidator;
|
||||
|
||||
public static function property(
|
||||
string $reference,
|
||||
?Validatable $validator = null,
|
||||
bool $mandatory = true
|
||||
): ChainedValidator;
|
||||
public static function property(string $propertyName, Validatable $validator): ChainedValidator;
|
||||
|
||||
public static function propertyExists(string $propertyName): ChainedValidator;
|
||||
|
||||
public static function propertyOptional(string $propertyName, Validatable $validator): ChainedValidator;
|
||||
|
||||
public static function base(int $base, ?string $chars = null): ChainedValidator;
|
||||
|
||||
|
|
|
@ -9,45 +9,36 @@ declare(strict_types=1);
|
|||
|
||||
namespace Respect\Validation\Rules;
|
||||
|
||||
use ReflectionProperty;
|
||||
use Respect\Validation\Attributes\ExceptionClass;
|
||||
use Respect\Validation\Exceptions\NonOmissibleValidationException;
|
||||
use Respect\Validation\Message\Template;
|
||||
use Respect\Validation\Helpers\CanBindEvaluateRule;
|
||||
use Respect\Validation\Helpers\CanExtractPropertyValue;
|
||||
use Respect\Validation\Result;
|
||||
use Respect\Validation\Rules\Core\Wrapper;
|
||||
use Respect\Validation\Validatable;
|
||||
|
||||
use function is_object;
|
||||
use function property_exists;
|
||||
|
||||
#[ExceptionClass(NonOmissibleValidationException::class)]
|
||||
#[Template(
|
||||
'Property {{name}} must be present',
|
||||
'Property {{name}} must not be present',
|
||||
self::TEMPLATE_NOT_PRESENT,
|
||||
)]
|
||||
#[Template(
|
||||
'Property {{name}} must be valid',
|
||||
'Property {{name}} must not validate',
|
||||
self::TEMPLATE_INVALID,
|
||||
)]
|
||||
final class Property extends AbstractRelated
|
||||
final class Property extends Wrapper
|
||||
{
|
||||
public function __construct(string $name, ?Validatable $rule = null, bool $mandatory = true)
|
||||
{
|
||||
parent::__construct($name, $rule, $mandatory);
|
||||
use CanBindEvaluateRule;
|
||||
use CanExtractPropertyValue;
|
||||
|
||||
public function __construct(
|
||||
private readonly string $propertyName,
|
||||
Validatable $rule,
|
||||
) {
|
||||
$rule->setName($rule->getName() ?? $propertyName);
|
||||
parent::__construct($rule);
|
||||
}
|
||||
|
||||
public function getReferenceValue(mixed $input): mixed
|
||||
public function evaluate(mixed $input): Result
|
||||
{
|
||||
$propertyMirror = new ReflectionProperty($input, (string) $this->getReference());
|
||||
if ($propertyMirror->isInitialized($input) === false) {
|
||||
return null;
|
||||
$propertyExistsResult = $this->bindEvaluate(new PropertyExists($this->propertyName), $this, $input);
|
||||
if (!$propertyExistsResult->isValid) {
|
||||
return $propertyExistsResult;
|
||||
}
|
||||
|
||||
return $propertyMirror->getValue($input);
|
||||
}
|
||||
$childResult = $this->rule->evaluate($this->extractPropertyValue($input, $this->propertyName));
|
||||
|
||||
public function hasReference(mixed $input): bool
|
||||
{
|
||||
return is_object($input) && property_exists($input, (string) $this->getReference());
|
||||
return (new Result($childResult->isValid, $input, $this))
|
||||
->withChildren($childResult)
|
||||
->withNameIfMissing($this->rule->getName() ?? $this->propertyName);
|
||||
}
|
||||
}
|
||||
|
|
40
library/Rules/PropertyExists.php
Normal file
40
library/Rules/PropertyExists.php
Normal file
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Respect\Validation\Rules;
|
||||
|
||||
use ReflectionObject;
|
||||
use Respect\Validation\Message\Template;
|
||||
use Respect\Validation\Result;
|
||||
use Respect\Validation\Rules\Core\Standard;
|
||||
|
||||
use function is_object;
|
||||
|
||||
#[Template(
|
||||
'{{name}} must be present',
|
||||
'{{name}} must not be present',
|
||||
)]
|
||||
final class PropertyExists extends Standard
|
||||
{
|
||||
public function __construct(
|
||||
private readonly string $propertyName
|
||||
) {
|
||||
}
|
||||
|
||||
public function evaluate(mixed $input): Result
|
||||
{
|
||||
if (!is_object($input)) {
|
||||
return Result::failed($input, $this)->withNameIfMissing($this->propertyName);
|
||||
}
|
||||
|
||||
$reflection = new ReflectionObject($input);
|
||||
|
||||
return new Result($reflection->hasProperty($this->propertyName), $input, $this, name: $this->propertyName);
|
||||
}
|
||||
}
|
44
library/Rules/PropertyOptional.php
Normal file
44
library/Rules/PropertyOptional.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\Helpers\CanBindEvaluateRule;
|
||||
use Respect\Validation\Helpers\CanExtractPropertyValue;
|
||||
use Respect\Validation\Result;
|
||||
use Respect\Validation\Rules\Core\Wrapper;
|
||||
use Respect\Validation\Validatable;
|
||||
|
||||
final class PropertyOptional extends Wrapper
|
||||
{
|
||||
use CanBindEvaluateRule;
|
||||
use CanExtractPropertyValue;
|
||||
|
||||
public function __construct(
|
||||
private readonly string $propertyName,
|
||||
Validatable $rule,
|
||||
) {
|
||||
$rule->setName($rule->getName() ?? $propertyName);
|
||||
parent::__construct($rule);
|
||||
}
|
||||
|
||||
public function evaluate(mixed $input): Result
|
||||
{
|
||||
$propertyExistsResult = $this->bindEvaluate(new PropertyExists($this->propertyName), $this, $input);
|
||||
if (!$propertyExistsResult->isValid) {
|
||||
return $propertyExistsResult->withInvertedMode();
|
||||
}
|
||||
|
||||
$childResult = $this->rule->evaluate($this->extractPropertyValue($input, $this->propertyName));
|
||||
|
||||
return (new Result($childResult->isValid, $input, $this))
|
||||
->withChildren($childResult)
|
||||
->withNameIfMissing($this->rule->getName() ?? $this->propertyName);
|
||||
}
|
||||
}
|
|
@ -27,20 +27,18 @@ exceptionFullMessage(static function (): void {
|
|||
->property(
|
||||
'mysql',
|
||||
v::create()
|
||||
->property('host', v::stringType(), true)
|
||||
->property('user', v::stringType(), true)
|
||||
->property('password', v::stringType(), true)
|
||||
->property('schema', v::stringType(), true),
|
||||
true
|
||||
->property('host', v::stringType())
|
||||
->property('user', v::stringType())
|
||||
->property('password', v::stringType())
|
||||
->property('schema', v::stringType())
|
||||
)
|
||||
->property(
|
||||
'postgresql',
|
||||
v::create()
|
||||
->property('host', v::stringType(), true)
|
||||
->property('user', v::stringType(), true)
|
||||
->property('password', v::stringType(), true)
|
||||
->property('schema', v::stringType(), true),
|
||||
true
|
||||
->property('host', v::stringType())
|
||||
->property('user', v::stringType())
|
||||
->property('password', v::stringType())
|
||||
->property('schema', v::stringType())
|
||||
)
|
||||
->setName('the given data')
|
||||
->assert($object);
|
||||
|
|
|
@ -18,9 +18,9 @@ exceptionFullMessage(static function (): void {
|
|||
?>
|
||||
--EXPECT--
|
||||
- All of the required rules must pass for `stdClass { +$author="foo" }`
|
||||
- Property title must be present
|
||||
- Property description must be present
|
||||
- title must be present
|
||||
- description must be present
|
||||
- All of the required rules must pass for author
|
||||
- author must be of type integer
|
||||
- author must have a length between 1 and 2
|
||||
- Property user must be present
|
||||
- user must be present
|
||||
|
|
167
tests/integration/rules/property.phpt
Normal file
167
tests/integration/rules/property.phpt
Normal file
|
@ -0,0 +1,167 @@
|
|||
--FILE--
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require 'vendor/autoload.php';
|
||||
|
||||
use Respect\Validation\Validator as v;
|
||||
|
||||
run([
|
||||
// Simple
|
||||
'Missing property' => [v::property('foo', v::intType()), new stdClass()],
|
||||
'Default' => [v::property('foo', v::intType()), (object) ['foo' => 'string']],
|
||||
'Negative' => [v::not(v::property('foo', v::intType())), (object) ['foo' => 12]],
|
||||
'Double-negative with missing property' => [
|
||||
v::not(v::not(v::property('foo', v::intType()))),
|
||||
new stdClass(),
|
||||
],
|
||||
|
||||
// With custom name
|
||||
'With wrapped name, missing property' => [
|
||||
v::property('foo', v::intType()->setName('Wrapped'))->setName('Wrapper'),
|
||||
new stdClass(),
|
||||
],
|
||||
'With wrapped name, default' => [
|
||||
v::property('foo', v::intType()->setName('Wrapped'))->setName('Wrapper'),
|
||||
(object) ['foo' => 'string'],
|
||||
],
|
||||
'With wrapped name, negative' => [
|
||||
v::not(v::property('foo', v::intType()->setName('Wrapped'))->setName('Wrapper'))->setName('Not'),
|
||||
(object) ['foo' => 12],
|
||||
],
|
||||
'With wrapper name, default' => [
|
||||
v::property('foo', v::intType())->setName('Wrapper'),
|
||||
(object) ['foo' => 'string'],
|
||||
],
|
||||
'With wrapper name, missing property' => [
|
||||
v::property('foo', v::intType())->setName('Wrapper'),
|
||||
new stdClass(),
|
||||
],
|
||||
'With wrapper name, negative' => [
|
||||
v::not(v::property('foo', v::intType())->setName('Wrapper'))->setName('Not'),
|
||||
(object) ['foo' => 12],
|
||||
],
|
||||
'With "Not" name, negative' => [
|
||||
v::not(v::property('foo', v::intType()))->setName('Not'),
|
||||
(object) ['foo' => 12],
|
||||
],
|
||||
|
||||
// With custom template
|
||||
'With template, default' => [
|
||||
v::property('foo', v::intType()),
|
||||
(object) ['foo' => 'string'],
|
||||
'Particularly precautions perplexing property',
|
||||
],
|
||||
'With template, negative' => [
|
||||
v::not(v::property('foo', v::intType())),
|
||||
(object) ['foo' => 12],
|
||||
'Not a prompt prospect of a particularly primitive property',
|
||||
],
|
||||
]);
|
||||
?>
|
||||
--EXPECT--
|
||||
Missing property
|
||||
⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺
|
||||
foo must be present
|
||||
- foo must be present
|
||||
[
|
||||
'foo' => 'foo must be present',
|
||||
]
|
||||
|
||||
Default
|
||||
⎺⎺⎺⎺⎺⎺⎺
|
||||
foo must be of type integer
|
||||
- foo must be of type integer
|
||||
[
|
||||
'foo' => 'foo must be of type integer',
|
||||
]
|
||||
|
||||
Negative
|
||||
⎺⎺⎺⎺⎺⎺⎺⎺
|
||||
foo must not be of type integer
|
||||
- foo must not be of type integer
|
||||
[
|
||||
'foo' => 'foo must not be of type integer',
|
||||
]
|
||||
|
||||
Double-negative with missing property
|
||||
⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺
|
||||
foo must be present
|
||||
- foo must be present
|
||||
[
|
||||
'foo' => 'foo must be present',
|
||||
]
|
||||
|
||||
With wrapped name, missing property
|
||||
⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺
|
||||
Wrapped must be present
|
||||
- Wrapped must be present
|
||||
[
|
||||
'Wrapped' => 'Wrapped must be present',
|
||||
]
|
||||
|
||||
With wrapped name, default
|
||||
⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺
|
||||
Wrapped must be of type integer
|
||||
- Wrapped must be of type integer
|
||||
[
|
||||
'Wrapped' => 'Wrapped must be of type integer',
|
||||
]
|
||||
|
||||
With wrapped 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 wrapper name, default
|
||||
⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺
|
||||
foo must be of type integer
|
||||
- foo must be of type integer
|
||||
[
|
||||
'foo' => 'foo must be of type integer',
|
||||
]
|
||||
|
||||
With wrapper name, missing property
|
||||
⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺
|
||||
foo must be present
|
||||
- foo must be present
|
||||
[
|
||||
'foo' => 'foo must be present',
|
||||
]
|
||||
|
||||
With wrapper name, negative
|
||||
⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺
|
||||
foo must not be of type integer
|
||||
- foo must not be of type integer
|
||||
[
|
||||
'foo' => 'foo must not be of type integer',
|
||||
]
|
||||
|
||||
With "Not" name, negative
|
||||
⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺
|
||||
foo must not be of type integer
|
||||
- foo must not be of type integer
|
||||
[
|
||||
'foo' => 'foo must not be of type integer',
|
||||
]
|
||||
|
||||
With template, default
|
||||
⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺
|
||||
Particularly precautions perplexing property
|
||||
- Particularly precautions perplexing property
|
||||
[
|
||||
'foo' => 'Particularly precautions perplexing property',
|
||||
]
|
||||
|
||||
With template, negative
|
||||
⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺
|
||||
Not a prompt prospect of a particularly primitive property
|
||||
- Not a prompt prospect of a particularly primitive property
|
||||
[
|
||||
'foo' => 'Not a prompt prospect of a particularly primitive property',
|
||||
]
|
||||
|
49
tests/integration/rules/propertyExists.phpt
Normal file
49
tests/integration/rules/propertyExists.phpt
Normal file
|
@ -0,0 +1,49 @@
|
|||
--FILE--
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require 'vendor/autoload.php';
|
||||
|
||||
use Respect\Validation\Validator as v;
|
||||
|
||||
run([
|
||||
'Default mode' => [v::propertyExists('foo'), (object) ['bar' => 'baz']],
|
||||
'Negative mode' => [v::not(v::propertyExists('foo')), (object) ['foo' => 'baz']],
|
||||
'Custom name' => [v::propertyExists('foo')->setName('Custom name'), (object) ['bar' => 'baz']],
|
||||
'Custom template' => [v::propertyExists('foo'), (object) ['bar' => 'baz'], 'Custom template for `{{name}}`'],
|
||||
]);
|
||||
?>
|
||||
--EXPECT--
|
||||
Default mode
|
||||
⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺
|
||||
foo must be present
|
||||
- foo must be present
|
||||
[
|
||||
'foo' => 'foo must be present',
|
||||
]
|
||||
|
||||
Negative mode
|
||||
⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺
|
||||
foo must not be present
|
||||
- foo must not be present
|
||||
[
|
||||
'foo' => 'foo must not be present',
|
||||
]
|
||||
|
||||
Custom name
|
||||
⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺
|
||||
Custom name must be present
|
||||
- Custom name must be present
|
||||
[
|
||||
'Custom name' => 'Custom name must be present',
|
||||
]
|
||||
|
||||
Custom template
|
||||
⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺
|
||||
Custom template for `foo`
|
||||
- Custom template for `foo`
|
||||
[
|
||||
'foo' => 'Custom template for `foo`',
|
||||
]
|
||||
|
134
tests/integration/rules/propertyOptional.phpt
Normal file
134
tests/integration/rules/propertyOptional.phpt
Normal file
|
@ -0,0 +1,134 @@
|
|||
--FILE--
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require 'vendor/autoload.php';
|
||||
|
||||
use Respect\Validation\Validator as v;
|
||||
|
||||
run([
|
||||
// Simple
|
||||
'Default' => [v::propertyOptional('foo', v::intType()), (object) ['foo' => 'string']],
|
||||
'Negative' => [v::not(v::propertyOptional('foo', v::intType())), (object) ['foo' => 12]],
|
||||
'Negative with missing property' => [
|
||||
v::not(v::propertyOptional('foo', v::intType())),
|
||||
new stdClass(),
|
||||
],
|
||||
|
||||
// With custom name
|
||||
'With wrapped name, default' => [
|
||||
v::propertyOptional('foo', v::intType()->setName('Wrapped'))->setName('Wrapper'),
|
||||
(object) ['foo' => 'string'],
|
||||
],
|
||||
'With wrapped name, negative' => [
|
||||
v::not(v::propertyOptional('foo', v::intType()->setName('Wrapped'))->setName('Wrapper'))->setName('Not'),
|
||||
(object) ['foo' => 12],
|
||||
],
|
||||
'With wrapper name, default' => [
|
||||
v::propertyOptional('foo', v::intType())->setName('Wrapper'),
|
||||
(object) ['foo' => 'string'],
|
||||
],
|
||||
'With wrapper name, negative' => [
|
||||
v::not(v::propertyOptional('foo', v::intType())->setName('Wrapper'))->setName('Not'),
|
||||
(object) ['foo' => 12],
|
||||
],
|
||||
'With "Not" name, negative' => [
|
||||
v::not(v::propertyOptional('foo', v::intType()))->setName('Not'),
|
||||
(object) ['foo' => 12],
|
||||
],
|
||||
|
||||
// With custom template
|
||||
'With template, default' => [
|
||||
v::propertyOptional('foo', v::intType()),
|
||||
(object) ['foo' => 'string'],
|
||||
'Proper property planners plan precise property plots',
|
||||
],
|
||||
'With template, negative' => [
|
||||
v::not(v::propertyOptional('foo', v::intType())),
|
||||
(object) ['foo' => 12],
|
||||
'Not proving prudent property planning promotes prosperity',
|
||||
],
|
||||
]);
|
||||
?>
|
||||
--EXPECT--
|
||||
Default
|
||||
⎺⎺⎺⎺⎺⎺⎺
|
||||
foo must be of type integer
|
||||
- foo must be of type integer
|
||||
[
|
||||
'foo' => 'foo must be of type integer',
|
||||
]
|
||||
|
||||
Negative
|
||||
⎺⎺⎺⎺⎺⎺⎺⎺
|
||||
foo must not be of type integer
|
||||
- foo must not be of type integer
|
||||
[
|
||||
'foo' => 'foo must not be of type integer',
|
||||
]
|
||||
|
||||
Negative with missing property
|
||||
⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺
|
||||
foo must be present
|
||||
- foo must be present
|
||||
[
|
||||
'foo' => 'foo must be present',
|
||||
]
|
||||
|
||||
With wrapped name, default
|
||||
⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺
|
||||
Wrapped must be of type integer
|
||||
- Wrapped must be of type integer
|
||||
[
|
||||
'Wrapped' => 'Wrapped must be of type integer',
|
||||
]
|
||||
|
||||
With wrapped 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 wrapper name, default
|
||||
⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺
|
||||
foo must be of type integer
|
||||
- foo must be of type integer
|
||||
[
|
||||
'foo' => 'foo must be of type integer',
|
||||
]
|
||||
|
||||
With wrapper name, negative
|
||||
⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺
|
||||
foo must not be of type integer
|
||||
- foo must not be of type integer
|
||||
[
|
||||
'foo' => 'foo must not be of type integer',
|
||||
]
|
||||
|
||||
With "Not" name, negative
|
||||
⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺
|
||||
foo must not be of type integer
|
||||
- foo must not be of type integer
|
||||
[
|
||||
'foo' => 'foo must not be of type integer',
|
||||
]
|
||||
|
||||
With template, default
|
||||
⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺
|
||||
Proper property planners plan precise property plots
|
||||
- Proper property planners plan precise property plots
|
||||
[
|
||||
'foo' => 'Proper property planners plan precise property plots',
|
||||
]
|
||||
|
||||
With template, negative
|
||||
⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺
|
||||
Not proving prudent property planning promotes prosperity
|
||||
- Not proving prudent property planning promotes prosperity
|
||||
[
|
||||
'foo' => 'Not proving prudent property planning promotes prosperity',
|
||||
]
|
||||
|
|
@ -11,9 +11,13 @@ namespace Respect\Validation\Test\Stubs;
|
|||
|
||||
final class WithProperties
|
||||
{
|
||||
public string $public = 'public';
|
||||
public const PUBLIC_VALUE = 'public';
|
||||
public const PROTECTED_VALUE = 'protected';
|
||||
public const PRIVATE_VALUE = 'private';
|
||||
|
||||
protected string $protected = 'protected';
|
||||
public string $public = self::PUBLIC_VALUE;
|
||||
|
||||
private string $private = 'private'; // @phpstan-ignore-line
|
||||
protected string $protected = self::PROTECTED_VALUE;
|
||||
|
||||
private string $private = self::PRIVATE_VALUE; // @phpstan-ignore-line
|
||||
}
|
||||
|
|
23
tests/library/Stubs/WithStaticProperties.php
Normal file
23
tests/library/Stubs/WithStaticProperties.php
Normal file
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Respect\Validation\Test\Stubs;
|
||||
|
||||
final class WithStaticProperties
|
||||
{
|
||||
public const PUBLIC_VALUE = 'public';
|
||||
public const PROTECTED_VALUE = 'protected';
|
||||
public const PRIVATE_VALUE = 'private';
|
||||
|
||||
public static string $public = self::PUBLIC_VALUE;
|
||||
|
||||
protected static string $protected = self::PROTECTED_VALUE;
|
||||
|
||||
private static string $private = self::PRIVATE_VALUE; // @phpstan-ignore-line
|
||||
}
|
|
@ -11,6 +11,9 @@ namespace Respect\Validation\Test;
|
|||
|
||||
use ArrayObject;
|
||||
use PHPUnit\Framework\TestCase as PHPUnitTestCase;
|
||||
use Respect\Validation\Test\Stubs\WithProperties;
|
||||
use Respect\Validation\Test\Stubs\WithStaticProperties;
|
||||
use Respect\Validation\Test\Stubs\WithUninitialized;
|
||||
use Respect\Validation\Validatable;
|
||||
use stdClass;
|
||||
|
||||
|
@ -259,4 +262,27 @@ abstract class TestCase extends PHPUnitTestCase
|
|||
'string key with false for a value' => ['foo', ['foo' => false]],
|
||||
];
|
||||
}
|
||||
|
||||
/** @return array<array{string, object}> */
|
||||
public static function providerForObjectsWithMissingProperties(): array
|
||||
{
|
||||
return [
|
||||
'object with no properties' => ['something', new stdClass()],
|
||||
'object with missing property' => ['nonExisting', new WithProperties()],
|
||||
];
|
||||
}
|
||||
|
||||
/** @return array<array{string, object}> */
|
||||
public static function providerForObjectsWithExistingProperties(): array
|
||||
{
|
||||
return [
|
||||
'public' => ['public', new WithProperties()],
|
||||
'protected' => ['protected', new WithProperties()],
|
||||
'private' => ['private', new WithProperties()],
|
||||
'uninitialized' => ['uninitialized', new WithUninitialized()],
|
||||
'static public' => ['public', new WithStaticProperties()],
|
||||
'static protected' => ['protected', new WithStaticProperties()],
|
||||
'static private' => ['private', new WithStaticProperties()],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
40
tests/unit/Rules/PropertyExistsTest.php
Normal file
40
tests/unit/Rules/PropertyExistsTest.php
Normal file
|
@ -0,0 +1,40 @@
|
|||
<?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\Test;
|
||||
use Respect\Validation\Test\TestCase;
|
||||
|
||||
#[CoversClass(PropertyExists::class)]
|
||||
final class PropertyExistsTest extends TestCase
|
||||
{
|
||||
#[Test]
|
||||
#[DataProvider('providerForScalarValues')]
|
||||
public function itShouldAlwaysInvalidateNonObjectValues(mixed $input): void
|
||||
{
|
||||
self::assertInvalidInput(new PropertyExists('foo'), $input);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
#[DataProvider('providerForObjectsWithExistingProperties')]
|
||||
public function itShouldValidateExistingProperties(string $propertyName, object $object): void
|
||||
{
|
||||
self::assertValidInput(new PropertyExists($propertyName), $object);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
#[DataProvider('providerForObjectsWithMissingProperties')]
|
||||
public function itShouldInvalidateMissingProperties(string $propertyName, object $object): void
|
||||
{
|
||||
self::assertInvalidInput(new PropertyExists($propertyName), $object);
|
||||
}
|
||||
}
|
89
tests/unit/Rules/PropertyOptionalTest.php
Normal file
89
tests/unit/Rules/PropertyOptionalTest.php
Normal file
|
@ -0,0 +1,89 @@
|
|||
<?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\Test\Rules\Stub;
|
||||
use Respect\Validation\Test\TestCase;
|
||||
use stdClass;
|
||||
|
||||
#[Group('rule')]
|
||||
#[CoversClass(PropertyOptional::class)]
|
||||
final class PropertyOptionalTest extends TestCase
|
||||
{
|
||||
#[Test]
|
||||
#[DataProvider('providerForScalarValues')]
|
||||
public function itShouldAlwaysValidateNonObjectValues(mixed $input): void
|
||||
{
|
||||
self::assertValidInput(new PropertyOptional('foo', Stub::daze()), $input);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
#[DataProvider('providerForObjectsWithMissingProperties')]
|
||||
public function itShouldAlwaysValidateMissingProperties(string $propertyName, object $object): void
|
||||
{
|
||||
self::assertValidInput(new PropertyOptional($propertyName, Stub::daze()), $object);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
#[DataProvider('providerForObjectsWithExistingProperties')]
|
||||
public function itShouldValidateExistingPropertiesWithWrappedRule(string $propertyName, object $object): void
|
||||
{
|
||||
self::assertValidInput(new PropertyOptional($propertyName, Stub::pass(1)), $object);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
#[DataProvider('providerForObjectsWithExistingProperties')]
|
||||
public function itShouldInvalidateExistingPropertiesWithWrappedRule(string $propertyName, object $object): void
|
||||
{
|
||||
self::assertInvalidInput(new PropertyOptional($propertyName, Stub::fail(1)), $object);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itShouldValidatePropertyWithTheWrappedRule(): void
|
||||
{
|
||||
$object = new stdClass();
|
||||
$object->foo = 'bar';
|
||||
|
||||
$wrapped = Stub::pass(1);
|
||||
|
||||
$rule = new PropertyOptional('foo', $wrapped);
|
||||
$rule->evaluate($object);
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
|
@ -10,64 +10,80 @@ 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\Test\Rules\Stub;
|
||||
use Respect\Validation\Test\RuleTestCase;
|
||||
use Respect\Validation\Test\Stubs\WithProperties;
|
||||
use Respect\Validation\Test\Stubs\WithUninitialized;
|
||||
use Respect\Validation\Test\TestCase;
|
||||
use stdClass;
|
||||
|
||||
#[Group('rule')]
|
||||
#[CoversClass(AbstractRelated::class)]
|
||||
#[CoversClass(Property::class)]
|
||||
final class PropertyTest extends RuleTestCase
|
||||
final class PropertyTest extends TestCase
|
||||
{
|
||||
/** @return iterable<string, array{Property, mixed}> */
|
||||
public static function providerForValidInput(): iterable
|
||||
#[Test]
|
||||
#[DataProvider('providerForScalarValues')]
|
||||
public function itShouldAlwaysInvalidateNonObjectValues(mixed $input): void
|
||||
{
|
||||
return [
|
||||
'attribute is present without extra validator' => [new Property('public'), new WithProperties()],
|
||||
'private attribute is present without extra validator' => [
|
||||
new Property('private'),
|
||||
new WithProperties(),
|
||||
],
|
||||
'attribute is present with extra validator' => [
|
||||
new Property('public', Stub::pass(1)),
|
||||
new WithProperties(),
|
||||
],
|
||||
'attribute is present but uninitialized' => [
|
||||
new Property('uninitialized'),
|
||||
new WithUninitialized(),
|
||||
],
|
||||
'non mandatory attribute is not present' => [
|
||||
new Property('nonexistent', null, false),
|
||||
new WithProperties(),
|
||||
],
|
||||
'non mandatory attribute is not present with extra validator' => [
|
||||
new Property('nonexistent', Stub::daze(), false),
|
||||
new WithProperties(),
|
||||
],
|
||||
'attribute is present but uninitialized with extra validator' => [
|
||||
new Property('uninitialized', Stub::pass(1)),
|
||||
new WithUninitialized(),
|
||||
],
|
||||
];
|
||||
self::assertInvalidInput(new Property('foo', Stub::daze()), $input);
|
||||
}
|
||||
|
||||
/** @return iterable<string, array{Property, mixed}> */
|
||||
public static function providerForInvalidInput(): iterable
|
||||
#[Test]
|
||||
#[DataProvider('providerForObjectsWithMissingProperties')]
|
||||
public function itShouldAlwaysInvalidateMissingProperties(string $propertyName, object $object): void
|
||||
{
|
||||
return [
|
||||
'attribute is absent without extra validator' => [new Property('barr'), new WithProperties()],
|
||||
'attribute is absent with extra validator' => [new Property('barr', Stub::daze()), new WithProperties()],
|
||||
'private attribute is not valid based on extra validator' => [
|
||||
new Property('private', Stub::fail(1)),
|
||||
new WithProperties(),
|
||||
],
|
||||
'value provided is an empty string' => [new Property('barr'), ''],
|
||||
'validator related to attribute does not validate' => [
|
||||
new Property('public', Stub::fail(1)),
|
||||
new WithProperties(),
|
||||
],
|
||||
];
|
||||
self::assertInvalidInput(new Property($propertyName, Stub::fail(1)), $object);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
#[DataProvider('providerForObjectsWithExistingProperties')]
|
||||
public function itShouldValidateExistingPropertiesWithWrappedRule(string $propertyName, object $object): void
|
||||
{
|
||||
self::assertValidInput(new Property($propertyName, Stub::pass(1)), $object);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
#[DataProvider('providerForObjectsWithExistingProperties')]
|
||||
public function itShouldInvalidateExistingPropertiesWithWrappedRule(string $propertyName, object $object): void
|
||||
{
|
||||
self::assertInvalidInput(new Property($propertyName, Stub::fail(1)), $object);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itShouldValidatePropertyWithTheWrappedRule(): void
|
||||
{
|
||||
$object = new stdClass();
|
||||
$object->foo = 'bar';
|
||||
|
||||
$wrapped = Stub::pass(1);
|
||||
|
||||
$rule = new Property('foo', $wrapped);
|
||||
$rule->evaluate($object);
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue