mirror of
https://github.com/Respect/Validation.git
synced 2024-05-29 03:42:25 +02:00
Apply contribution guidelines to "Attribute" rule
Signed-off-by: Emmerson Siqueira <emmersonsiqueira@gmail.com>
This commit is contained in:
parent
51ec5e1b95
commit
0a031649a8
|
@ -4,7 +4,7 @@
|
|||
- `Attribute(string $name, Validatable $rule)`
|
||||
- `Attribute(string $name, Validatable $rule, bool $mandatory)`
|
||||
|
||||
Validates an object attribute.
|
||||
Validates an object attribute, even private ones.
|
||||
|
||||
```php
|
||||
$obj = new stdClass;
|
||||
|
|
|
@ -13,11 +13,20 @@ declare(strict_types=1);
|
|||
|
||||
namespace Respect\Validation\Exceptions;
|
||||
|
||||
class AttributeException extends NestedValidationException implements NonOmissibleException
|
||||
/**
|
||||
* Exceptions to be thrown by the Attribute Rule.
|
||||
*
|
||||
* @author Emmerson Siqueira <emmersonsiqueira@gmail.com>
|
||||
* @author Henrique Moody <henriquemoody@gmail.com>
|
||||
*/
|
||||
final class AttributeException extends NestedValidationException implements NonOmissibleException
|
||||
{
|
||||
public const NOT_PRESENT = 'not_present';
|
||||
public const INVALID = 'invalid';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $defaultTemplates = [
|
||||
self::MODE_DEFAULT => [
|
||||
self::NOT_PRESENT => 'Attribute {{name}} must be present',
|
||||
|
@ -29,6 +38,9 @@ class AttributeException extends NestedValidationException implements NonOmissib
|
|||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function chooseTemplate(): string
|
||||
{
|
||||
return $this->getParam('hasReference') ? static::INVALID : static::NOT_PRESENT;
|
||||
|
|
|
@ -13,8 +13,20 @@ declare(strict_types=1);
|
|||
|
||||
namespace Respect\Validation\Exceptions;
|
||||
|
||||
class KeyException extends AttributeException implements NonOmissibleException
|
||||
/**
|
||||
* Exceptions to be thrown by the Attribute Rule.
|
||||
*
|
||||
* @author Emmerson Siqueira <emmersonsiqueira@gmail.com>
|
||||
* @author Henrique Moody <henriquemoody@gmail.com>
|
||||
*/
|
||||
final class KeyException extends NestedValidationException implements NonOmissibleException
|
||||
{
|
||||
public const NOT_PRESENT = 'not_present';
|
||||
public const INVALID = 'invalid';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $defaultTemplates = [
|
||||
self::MODE_DEFAULT => [
|
||||
self::NOT_PRESENT => 'Key {{name}} must be present',
|
||||
|
@ -25,4 +37,12 @@ class KeyException extends AttributeException implements NonOmissibleException
|
|||
self::INVALID => 'Key {{name}} must not be valid',
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function chooseTemplate(): string
|
||||
{
|
||||
return $this->getParam('hasReference') ? static::INVALID : static::NOT_PRESENT;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,8 +13,20 @@ declare(strict_types=1);
|
|||
|
||||
namespace Respect\Validation\Exceptions;
|
||||
|
||||
class KeyNestedException extends AttributeException implements NonOmissibleException
|
||||
/**
|
||||
* Exceptions to be thrown by the Attribute Rule.
|
||||
*
|
||||
* @author Emmerson Siqueira <emmersonsiqueira@gmail.com>
|
||||
* @author Henrique Moody <henriquemoody@gmail.com>
|
||||
*/
|
||||
final class KeyNestedException extends NestedValidationException implements NonOmissibleException
|
||||
{
|
||||
public const NOT_PRESENT = 'not_present';
|
||||
public const INVALID = 'invalid';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $defaultTemplates = [
|
||||
self::MODE_DEFAULT => [
|
||||
self::NOT_PRESENT => 'No items were found for key chain {{name}}',
|
||||
|
@ -25,4 +37,12 @@ class KeyNestedException extends AttributeException implements NonOmissibleExcep
|
|||
self::INVALID => 'Key chain {{name}} must not be valid',
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function chooseTemplate(): string
|
||||
{
|
||||
return $this->getParam('hasReference') ? static::INVALID : static::NOT_PRESENT;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ abstract class AbstractRelated extends AbstractRule
|
|||
public $reference = '';
|
||||
public $validator;
|
||||
|
||||
abstract public function hasReference($input);
|
||||
abstract public function hasReference($input): bool;
|
||||
|
||||
abstract public function getReferenceValue($input);
|
||||
|
||||
|
@ -52,13 +52,6 @@ abstract class AbstractRelated extends AbstractRule
|
|||
return $this;
|
||||
}
|
||||
|
||||
private function decision($type, $hasReference, $input)
|
||||
{
|
||||
return (!$this->mandatory && !$hasReference)
|
||||
|| (is_null($this->validator)
|
||||
|| $this->validator->$type($this->getReferenceValue($input)));
|
||||
}
|
||||
|
||||
public function assert($input): void
|
||||
{
|
||||
$hasReference = $this->hasReference($input);
|
||||
|
@ -94,4 +87,11 @@ abstract class AbstractRelated extends AbstractRule
|
|||
|
||||
return $this->decision('validate', $hasReference, $input);
|
||||
}
|
||||
|
||||
private function decision(string $type, bool $hasReference, $input)
|
||||
{
|
||||
return (!$this->mandatory && !$hasReference)
|
||||
|| (is_null($this->validator)
|
||||
|| $this->validator->$type($this->getReferenceValue($input)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,21 +13,31 @@ declare(strict_types=1);
|
|||
|
||||
namespace Respect\Validation\Rules;
|
||||
|
||||
use ReflectionException;
|
||||
use ReflectionProperty;
|
||||
use Respect\Validation\Exceptions\ComponentException;
|
||||
use Respect\Validation\Validatable;
|
||||
|
||||
class Attribute extends AbstractRelated
|
||||
/**
|
||||
* Validates an object attribute, event private ones.
|
||||
*
|
||||
* @author Alexandre Gomes Gaigalas <alexandre@gaigalas.net>
|
||||
* @author Emmerson Siqueira <emmersonsiqueira@gmail.com>
|
||||
* @author Henrique Moody <henriquemoody@gmail.com>
|
||||
*/
|
||||
final class Attribute extends AbstractRelated
|
||||
{
|
||||
public function __construct($reference, Validatable $validator = null, $mandatory = true)
|
||||
public function __construct(string $reference, Validatable $validator = null, $mandatory = true)
|
||||
{
|
||||
if (!is_string($reference) || empty($reference)) {
|
||||
throw new ComponentException('Invalid attribute/property name');
|
||||
}
|
||||
|
||||
parent::__construct($reference, $validator, $mandatory);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param object $input
|
||||
*
|
||||
* @throws ReflectionException
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getReferenceValue($input)
|
||||
{
|
||||
$propertyMirror = new ReflectionProperty($input, $this->reference);
|
||||
|
@ -36,7 +46,10 @@ class Attribute extends AbstractRelated
|
|||
return $propertyMirror->getValue($input);
|
||||
}
|
||||
|
||||
public function hasReference($input)
|
||||
/**
|
||||
* @param object $input
|
||||
*/
|
||||
public function hasReference($input): bool
|
||||
{
|
||||
return is_object($input) && property_exists($input, $this->reference);
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ class Call extends AbstractRelated
|
|||
return call_user_func_array($this->reference, [&$input]);
|
||||
}
|
||||
|
||||
public function hasReference($input)
|
||||
public function hasReference($input): bool
|
||||
{
|
||||
return is_callable($this->reference);
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ class Key extends AbstractRelated
|
|||
return $input[$this->reference];
|
||||
}
|
||||
|
||||
public function hasReference($input)
|
||||
public function hasReference($input): bool
|
||||
{
|
||||
return is_array($input) && array_key_exists($this->reference, $input);
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ use Respect\Validation\Exceptions\ComponentException;
|
|||
|
||||
class KeyNested extends AbstractRelated
|
||||
{
|
||||
public function hasReference($input)
|
||||
public function hasReference($input): bool
|
||||
{
|
||||
try {
|
||||
$this->getReferenceValue($input);
|
||||
|
|
|
@ -13,168 +13,61 @@ declare(strict_types=1);
|
|||
|
||||
namespace Respect\Validation\Rules;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Respect\Validation\Test\RuleTestCase;
|
||||
use Respect\Validation\Validatable;
|
||||
|
||||
class PrivClass
|
||||
{
|
||||
public const PROPERTY_VALUE = 'foo';
|
||||
|
||||
private $bar = self::PROPERTY_VALUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @group rule
|
||||
* @covers \Respect\Validation\Rules\Attribute
|
||||
* @covers \Respect\Validation\Exceptions\AttributeException
|
||||
*/
|
||||
class AttributeTest extends TestCase
|
||||
final class AttributeTest extends RuleTestCase
|
||||
{
|
||||
public function testAttributeWithNoExtraValidationShouldCheckItsPresence(): void
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function providerForValidInput(): array
|
||||
{
|
||||
$validator = new Attribute('bar');
|
||||
$obj = new \stdClass();
|
||||
$obj->bar = 'foo';
|
||||
$validator->check($obj);
|
||||
self::assertTrue($validator->__invoke($obj));
|
||||
$validator->assert($obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Respect\Validation\Exceptions\AttributeException
|
||||
*/
|
||||
public function testAbsentAttributeShouldRaiseAttributeException(): void
|
||||
{
|
||||
$validator = new Attribute('bar');
|
||||
$obj = new \stdClass();
|
||||
$obj->baraaaaa = 'foo';
|
||||
self::assertFalse($validator->__invoke($obj));
|
||||
$validator->assert($obj);
|
||||
}
|
||||
$extraValidator = $this->createMock(Validatable::class);
|
||||
$extraValidator->method('validate')
|
||||
->willReturn(true);
|
||||
|
||||
/**
|
||||
* @expectedException \Respect\Validation\Exceptions\ValidationException
|
||||
*/
|
||||
public function testAbsentAttributeShouldRaiseAttributeException_on_check(): void
|
||||
{
|
||||
$validator = new Attribute('bar');
|
||||
$obj = new \stdClass();
|
||||
$obj->baraaaaa = 'foo';
|
||||
self::assertFalse($validator->__invoke($obj));
|
||||
$validator->check($obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider providerForInvalidAttributeNames
|
||||
* @expectedException \Respect\Validation\Exceptions\ComponentException
|
||||
*/
|
||||
public function testInvalidConstructorArgumentsShouldThrowComponentException($attributeName): void
|
||||
{
|
||||
$validator = new Attribute($attributeName);
|
||||
}
|
||||
|
||||
public function providerForInvalidAttributeNames()
|
||||
{
|
||||
return [
|
||||
[new \stdClass()],
|
||||
[123],
|
||||
[''],
|
||||
'Is valid when attribute is present without extra validator' => [new Attribute('bar'), $obj],
|
||||
'Is valid when private attribute is present without extra validator' => [new Attribute('bar'), $this->objectWithPrivateProperty()],
|
||||
'Is valid when attribute is present with extra validator' => [new Attribute('bar', $extraValidator), $obj],
|
||||
'Is valid when non mandatory attribute is not present' => [new Attribute('foo', null, false), $obj],
|
||||
'Is valid when non mandatory attribute is not present with extra validator' => [new Attribute('foo', $extraValidator, false), $obj],
|
||||
];
|
||||
}
|
||||
|
||||
public function testExtraValidatorRulesForAttribute(): void
|
||||
{
|
||||
$subValidator = new Length(1, 3);
|
||||
$validator = new Attribute('bar', $subValidator);
|
||||
$obj = new \stdClass();
|
||||
$obj->bar = 'foo';
|
||||
self::assertTrue($validator->__invoke($obj));
|
||||
$validator->assert($obj);
|
||||
$validator->check($obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Respect\Validation\Exceptions\AttributeException
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function testShouldNotValidateEmptyString(): void
|
||||
public function providerForInvalidInput(): array
|
||||
{
|
||||
$subValidator = new Length(1, 3);
|
||||
$validator = new Attribute('bar', $subValidator);
|
||||
$obj = new \stdClass();
|
||||
$obj->bar = 'foo';
|
||||
|
||||
self::assertFalse($validator->__invoke(''));
|
||||
$validator->assert('');
|
||||
$extraValidatorMock = $this->createMock(Validatable::class);
|
||||
$extraValidatorMock->method('validate')->willReturn(false);
|
||||
|
||||
return [
|
||||
'Is not valid when attribute is absent without extra validator' => [new Attribute('barr'), $obj],
|
||||
'Is not valid when private attribute is not valid based on extra validator' => [new Attribute('bar', $extraValidatorMock), $this->objectWithPrivateProperty()],
|
||||
'Is not valid when value provided is an empty string' => [new Attribute('barr'), ''],
|
||||
'Is not valid when validator related to attribute does not validate' => [new Attribute('bar', $extraValidatorMock), $obj],
|
||||
];
|
||||
}
|
||||
|
||||
public function testExtraValidatorRulesForAttribute_should_fail_if_invalid(): void
|
||||
private function objectWithPrivateProperty()
|
||||
{
|
||||
$subValidator = new Length(1, 3);
|
||||
$validator = new Attribute('bar', $subValidator);
|
||||
$obj = new \stdClass();
|
||||
$obj->bar = 'foo hey this has more than 3 chars';
|
||||
self::assertFalse($validator->__invoke($obj));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Respect\Validation\Exceptions\LengthException
|
||||
*/
|
||||
public function testExtraValidatorRulesForAttribute_should_raise_extra_validator_exception_on_check(): void
|
||||
{
|
||||
$subValidator = new Length(1, 3);
|
||||
$validator = new Attribute('bar', $subValidator);
|
||||
$obj = new \stdClass();
|
||||
$obj->bar = 'foo hey this has more than 3 chars';
|
||||
$validator->check($obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Respect\Validation\Exceptions\AttributeException
|
||||
*/
|
||||
public function testExtraValidatorRulesForAttribute_should_raise_AttributeException_on_assert(): void
|
||||
{
|
||||
$subValidator = new Length(1, 3);
|
||||
$validator = new Attribute('bar', $subValidator);
|
||||
$obj = new \stdClass();
|
||||
$obj->bar = 'foo hey this has more than 3 chars';
|
||||
$validator->assert($obj);
|
||||
}
|
||||
|
||||
public function testNotMandatoryAttributeShouldNotFailWhenAttributeIsAbsent(): void
|
||||
{
|
||||
$validator = new Attribute('bar', null, false);
|
||||
$obj = new \stdClass();
|
||||
self::assertTrue($validator->__invoke($obj));
|
||||
}
|
||||
|
||||
public function testNotMandatoryAttributeShouldNotFailWhenAttributeIsAbsent_with_extra_validator(): void
|
||||
{
|
||||
$subValidator = new Length(1, 3);
|
||||
$validator = new Attribute('bar', $subValidator, false);
|
||||
$obj = new \stdClass();
|
||||
self::assertTrue($validator->__invoke($obj));
|
||||
}
|
||||
|
||||
public function testPrivateAttributeShouldAlsoBeChecked(): void
|
||||
{
|
||||
$obj = new PrivClass();
|
||||
|
||||
$validatable = $this->createMock(Validatable::class);
|
||||
$validatable
|
||||
->expects($this->once())
|
||||
->method('assert')
|
||||
->with(PrivClass::PROPERTY_VALUE);
|
||||
|
||||
$validator = new Attribute('bar', $validatable);
|
||||
|
||||
$validator->assert($obj);
|
||||
}
|
||||
|
||||
public function testPrivateAttributeShouldFailIfNotValid(): void
|
||||
{
|
||||
$subValidator = new Length(33333, 888888);
|
||||
$validator = new Attribute('bar', $subValidator);
|
||||
$obj = new PrivClass();
|
||||
self::assertFalse($validator->__invoke($obj));
|
||||
return new class() {
|
||||
public const PROPERTY_VALUE = 'foo';
|
||||
private $bar = self::PROPERTY_VALUE;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue