Apply contribution guidelines to "Each" rule

Also removed the possibility of validating keys once it's possible to
reach the same behavior by combining this rule with "Call" rule.

Co-authored-by: Henrique Moody <henriquemoody@gmail.com>
This commit is contained in:
William Espindola 2018-06-16 20:20:47 -03:00 committed by Henrique Moody
parent 70eb87bd77
commit dc3951edf1
No known key found for this signature in database
GPG key ID: 221E9281655813A6
7 changed files with 185 additions and 140 deletions

View file

@ -1,11 +1,8 @@
# Each
- `Each(Validatable $ruleForValue)`
- `Each(null, Validatable $ruleForKey)`
- `Each(Validatable $ruleForValue, Validatable $ruleForKey)`
- `Each(Validatable $rule)`
Iterates over an array or Iterator and validates the value or key
of each entry:
Validates whether each value in the input is valid according to another rule.
```php
$releaseDates = [
@ -14,20 +11,37 @@ $releaseDates = [
'relational' => '2011-02-05',
];
v::arrayVal()->each(v::dateTime())->validate($releaseDates); // true
v::arrayVal()->each(v::dateTime(), v::stringType()->lowercase())->validate($releaseDates); // true
v::each(v::dateTime())->validate($releaseDates); // true
```
Using `arrayVal()` before `each()` is a best practice.
You can also validate array keys combining this rule with [Call](Call.md):
```php
v::call('array_keys', v::each(v::stringType()))->validate($releaseDates); // true
```
This rule will not validate values that are not iterable, to have a more detailed
error message, add [IterableType](IterableType.md) to your chain, for example.
If the input is empty this rule will consider the value as valid, you use
[NotEmpty](NotEmpty.md) if convenient:
```php
v::each(v::dateTime())->validate([]); // true
v::notEmpty()->each(v::dateTime())->validate([]); // false
```
## Changelog
Version | Description
--------|-------------
2.0.0 | Remove support for key validation
0.3.9 | Created
***
See also:
- [Key](Key.md)
- [ArrayVal](ArrayVal.md)
- [Call](Call.md)
- [IterableType](IterableType.md)
- [Key](Key.md)

View file

@ -13,8 +13,16 @@ declare(strict_types=1);
namespace Respect\Validation\Exceptions;
class EachException extends NestedValidationException
/**
* @author Alexandre Gomes Gaigalas <alexandre@gaigalas.net>
* @author Henrique Moody <henriquemoody@gmail.com>
* @author William Espindola <oi@williamespindola.com.br>
*/
final class EachException extends NestedValidationException
{
/**
* {@inheritdoc}
*/
public static $defaultTemplates = [
self::MODE_DEFAULT => [
self::STANDARD => 'Each item in {{name}} must be valid',

View file

@ -17,42 +17,48 @@ use Respect\Validation\Exceptions\ValidationException;
use Respect\Validation\Helpers\CanValidateIterable;
use Respect\Validation\Validatable;
class Each extends AbstractRule
/**
* Validates whether each value in the input is valid according to another rule.
*
* @author Alexandre Gomes Gaigalas <alexandre@gaigalas.net>
* @author Henrique Moody <henriquemoody@gmail.com>
* @author Nick Lombard <github@jigsoft.co.za>
* @author William Espindola <oi@williamespindola.com.br>
*/
final class Each extends AbstractRule
{
use CanValidateIterable;
public $itemValidator;
public $keyValidator;
/**
* @var Validatable
*/
private $rule;
public function __construct(Validatable $itemValidator = null, Validatable $keyValidator = null)
/**
* Initializes the constructor.
*
* @param mixed $rule
*/
public function __construct(Validatable $rule)
{
$this->itemValidator = $itemValidator;
$this->keyValidator = $keyValidator;
$this->rule = $rule;
}
/**
* {@inheritdoc}
*/
public function assert($input): void
{
$exceptions = [];
if (!$this->isIterable($input)) {
throw $this->reportError($input);
}
foreach ($input as $key => $item) {
if (isset($this->itemValidator)) {
try {
$this->itemValidator->assert($item);
} catch (ValidationException $e) {
$exceptions[] = $e;
}
}
if (isset($this->keyValidator)) {
try {
$this->keyValidator->assert($key);
} catch (ValidationException $e) {
$exceptions[] = $e;
}
$exceptions = [];
foreach ($input as $value) {
try {
$this->rule->check($value);
} catch (ValidationException $exception) {
$exceptions[] = $exception;
}
}
@ -61,39 +67,17 @@ class Each extends AbstractRule
}
}
public function check($input): void
{
if (!$this->isIterable($input)) {
throw $this->reportError($input);
}
foreach ($input as $key => $item) {
if (isset($this->itemValidator)) {
$this->itemValidator->check($item);
}
if (isset($this->keyValidator)) {
$this->keyValidator->check($key);
}
}
}
/**
* {@inheritdoc}
*/
public function validate($input): bool
{
if (!$this->isIterable($input)) {
try {
$this->assert($input);
} catch (ValidationException $exception) {
return false;
}
foreach ($input as $key => $item) {
if (isset($this->itemValidator) && !$this->itemValidator->validate($item)) {
return false;
}
if (isset($this->keyValidator) && !$this->keyValidator->validate($key)) {
return false;
}
}
return true;
}
}

View file

@ -55,7 +55,7 @@ use Respect\Validation\Rules\Key;
* @method static Validator digit(string $additionalChars = null)
* @method static Validator directory()
* @method static Validator domain(bool $tldCheck = true)
* @method static Validator each(Validatable $itemValidator = null, Validatable $keyValidator = null)
* @method static Validator each(Validatable $rule)
* @method static Validator email()
* @method static Validator endsWith($endValue, bool $identical = false)
* @method static Validator equals($compareTo)

View file

@ -0,0 +1,38 @@
--FILE--
<?php
require 'vendor/autoload.php';
use Respect\Validation\Exceptions\EachException;
use Respect\Validation\Exceptions\NestedValidationException;
use Respect\Validation\Validator as v;
try {
v::each(v::dateTime())->check(null);
} catch (EachException $exception) {
echo $exception->getMessage().PHP_EOL;
}
try {
v::not(v::each(v::dateTime()))->check(['2018-10-10']);
} catch (EachException $exception) {
echo $exception->getMessage().PHP_EOL;
}
try {
v::each(v::dateTime())->assert(null);
} catch (NestedValidationException $exception) {
echo $exception->getFullMessage().PHP_EOL;
}
try {
v::not(v::each(v::dateTime()))->assert(['2018-10-10']);
} catch (NestedValidationException $exception) {
echo $exception->getFullMessage().PHP_EOL;
}
?>
--EXPECTF--
Each item in `NULL` must be valid
Each item in `{ "2018-10-10" }` must not validate
- Each item in `NULL` must be valid
- Each item in `{ "2018-10-10" }` must not validate

View file

@ -126,7 +126,7 @@ abstract class RuleTestCase extends TestCase
*/
public function shouldValidateValidInput(Validatable $validator, $input): void
{
self::assertTrue($validator->validate($input));
self::assertValidInput($validator, $input);
}
/**
@ -139,6 +139,16 @@ abstract class RuleTestCase extends TestCase
*/
public function shouldValidateInvalidInput(Validatable $validator, $input): void
{
self::assertFalse($validator->validate($input));
self::assertInvalidInput($validator, $input);
}
public static function assertValidInput(Validatable $rule, $input): void
{
self::assertTrue($rule->validate($input));
}
public static function assertInvalidInput(Validatable $rule, $input): void
{
self::assertFalse($rule->validate($input));
}
}

View file

@ -14,44 +14,41 @@ declare(strict_types=1);
namespace Respect\Validation\Rules;
use Respect\Validation\Test\RuleTestCase;
use Respect\Validation\Validatable;
use SplStack;
use stdClass;
/**
* @group rule
* @group rule
*
* @covers \Respect\Validation\Rules\Each
* @covers \Respect\Validation\Exceptions\EachException
*
* @author Alexandre Gomes Gaigalas <alexandre@gaigalas.net>
* @author Emmerson <emmersonsiqueira@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
* @author William Espindola <oi@williamespindola.com.br>
*/
class EachTest extends RuleTestCase
final class EachTest extends RuleTestCase
{
/**
* {@inheritdoc}
*/
public function providerForValidInput(): array
{
$ruleNotEmpty = new Each($this->createValidatableMock(true));
$ruleAlphaItemIntKey = new Each($this->createValidatableMock(true), $this->createValidatableMock(true));
$ruleOnlyKeyValidation = new Each(null, $this->createValidatableMock(true));
$intStack = new \SplStack();
$intStack->push(1);
$intStack->push(2);
$intStack->push(3);
$intStack->push(4);
$intStack->push(5);
$stdClass = new \stdClass();
$stdClass->name = 'Emmerson';
$stdClass->age = 22;
$rule = new Each($this->createValidatableMock(true));
return [
[$ruleNotEmpty, [1, 2, 3, 4, 5]],
[$ruleNotEmpty, $intStack],
[$ruleNotEmpty, $stdClass],
[$ruleAlphaItemIntKey, ['a', 'b', 'c', 'd', 'e']],
[$ruleOnlyKeyValidation, ['a', 'b', 'c', 'd', 'e']],
[$rule, []],
[$rule, [1, 2, 3, 4, 5]],
];
}
/**
* {@inheritdoc}
*/
public function providerForInvalidInput(): array
{
$rule = new Each($this->createValidatableMock(false));
$ruleOnlyKeyValidation = new Each(null, $this->createValidatableMock(false));
return [
[$rule, 123],
@ -59,73 +56,67 @@ class EachTest extends RuleTestCase
[$rule, null],
[$rule, false],
[$rule, ['', 2, 3, 4, 5]],
[$ruleOnlyKeyValidation, ['age' => 22]],
[$rule, ['a', 2, 3, 4, 5]],
];
}
/**
* @doesNotPerformAssertions
* @test
*/
public function testValidatorShouldPassIfEveryArrayItemPass(): void
public function itShouldValidateTraversable(): void
{
$v = new Each($this->createValidatableMock(true));
$v->check([1, 2, 3, 4, 5]);
$v->assert([1, 2, 3, 4, 5]);
$validatable = $this->createMock(Validatable::class);
$validatable
->expects($this->at(0))
->method('check')
->with('A');
$validatable
->expects($this->at(1))
->method('check')
->with('B');
$validatable
->expects($this->at(2))
->method('check')
->with('C');
$rule = new Each($validatable);
$input = new SplStack();
$input->push('C');
$input->push('B');
$input->push('A');
self::assertValidInput($rule, $input);
}
/**
* @doesNotPerformAssertions
* @test
*/
public function testValidatorShouldPassIfEveryArrayItemAndKeyPass(): void
public function itShouldValidateStdClass(): void
{
$v = new Each($this->createValidatableMock(true), $this->createValidatableMock(true));
$v->check(['a', 'b', 'c', 'd', 'e']);
$v->assert(['a', 'b', 'c', 'd', 'e']);
}
$validatable = $this->createMock(Validatable::class);
/**
* @doesNotPerformAssertions
*/
public function testValidatorShouldPassWithOnlyKeyValidation(): void
{
$v = new Each(null, $this->createValidatableMock(true));
$v->check(['a', 'b', 'c', 'd', 'e']);
$v->assert(['a', 'b', 'c', 'd', 'e']);
}
$validatable
->expects($this->at(0))
->method('check')
->with(1);
$validatable
->expects($this->at(1))
->method('check')
->with(2);
$validatable
->expects($this->at(2))
->method('check')
->with(3);
/**
* @expectedException \Respect\Validation\Exceptions\EachException
*/
public function testValidatorShouldNotPassWithOnlyKeyValidation(): void
{
$v = new Each(null, $this->createValidatableMock(false));
$v->assert(['a', 'b', 'c', 'd', 'e']);
}
$rule = new Each($validatable);
/**
* @expectedException \Respect\Validation\Exceptions\EachException
*/
public function testAssertShouldFailOnInvalidItem(): void
{
$v = new Each($this->createValidatableMock(false));
$v->assert(['a', 2, 3, 4, 5]);
}
$input = new stdClass();
$input->foo = 1;
$input->bar = 2;
$input->baz = 3;
/**
* @expectedException \Respect\Validation\Exceptions\EachException
*/
public function testAssertShouldFailWithNonIterableInput(): void
{
$v = new Each($this->createValidatableMock(false));
$v->assert('a');
}
/**
* @expectedException \Respect\Validation\Exceptions\EachException
*/
public function testCheckShouldFailWithNonIterableInput(): void
{
$v = new Each($this->createValidatableMock(false));
$v->check(null);
self::assertValidInput($rule, $input);
}
}