Create "OneOf" rule

This commit is contained in:
Bradyn Poulsen 2016-12-04 02:06:32 -07:00 committed by Henrique Moody
parent 7ee4ae47d5
commit d48b55a092
No known key found for this signature in database
GPG key ID: 221E9281655813A6
9 changed files with 267 additions and 2 deletions

View file

@ -25,6 +25,7 @@ v::allOf(
***
See also:
* [OneOf](OneOf.md)
* [AnyOf](AnyOf.md)
* [NoneOf](NoneOf.md)
* [OneOf](OneOf.md)
* [When](When.md)

View file

@ -22,4 +22,5 @@ See also:
* [AllOf](AllOf.md)
* [NoneOf](NoneOf.md)
* [OneOf](OneOf.md)
* [When](When.md)

View file

@ -16,6 +16,7 @@ In the sample above, 'foo' isn't a integer nor a float, so noneOf returns true.
***
See also:
* [Not](Not.md)
* [AllOf](AllOf.md)
* [AnyOf](AnyOf.md)
* [Not](Not.md)
* [OneOf](OneOf.md)

22
docs/OneOf.md Normal file
View file

@ -0,0 +1,22 @@
# OneOf
- `v::oneOf(v $v1, v $v2, v $v3...)`
Will validate if exactly one inner validator passes.
```php
v::oneOf(v::digit(), v::alpha())->validate('AB'); // true
v::oneOf(v::digit(), v::alpha())->validate('12'); // true
v::oneOf(v::digit(), v::alpha())->validate('AB12'); // false
v::oneOf(v::digit(), v::alpha())->validate('*'); // false
```
The chains above validate if the input is either a digit or an alphabetic
character, one or the other, but not neither nor both.
***
See also:
* [AllOf](AllOf.md)
* [AnyOf](AnyOf.md)
* [NoneOf](NoneOf.md)

View file

@ -136,6 +136,7 @@
## Group Validators
* [AllOf](AllOf.md)
* [AnyOf](AnyOf.md)
* [NoneOf](NoneOf.md)
* [OneOf](OneOf.md)
@ -209,6 +210,7 @@
* [Alpha](Alpha.md)
* [AlwaysInvalid](AlwaysInvalid.md)
* [AlwaysValid](AlwaysValid.md)
* [AnyOf](AnyOf.md)
* [ArrayVal](ArrayVal.md)
* [ArrayType](ArrayType.md)
* [Attribute](Attribute.md)

View file

@ -0,0 +1,27 @@
<?php
/*
* This file is part of Respect/Validation.
*
* (c) Alexandre Gomes Gaigalas <alexandre@gaigalas.net>
*
* For the full copyright and license information, please view the "LICENSE.md"
* file that was distributed with this source code.
*/
namespace Respect\Validation\Exceptions;
/**
* @author Bradyn Poulsen <bradyn@bradynpoulsen.com>
*/
class OneOfException extends NestedValidationException
{
public static $defaultTemplates = [
self::MODE_DEFAULT => [
self::STANDARD => 'Only one of these rules must pass for {{name}}',
],
self::MODE_NEGATIVE => [
self::STANDARD => 'Only one of these rules must not pass for {{name}}',
],
];
}

69
library/Rules/OneOf.php Normal file
View file

@ -0,0 +1,69 @@
<?php
/*
* This file is part of Respect/Validation.
*
* (c) Alexandre Gomes Gaigalas <alexandre@gaigalas.net>
*
* For the full copyright and license information, please view the "LICENSE.md"
* file that was distributed with this source code.
*/
namespace Respect\Validation\Rules;
use Respect\Validation\Exceptions\ValidationException;
/**
* @author Bradyn Poulsen <bradyn@bradynpoulsen.com>
* @author Henrique Moody <henriquemoody@gmail.com>
*/
class OneOf extends AbstractComposite
{
public function assert($input)
{
$validators = $this->getRules();
$exceptions = $this->validateRules($input);
$numRules = count($validators);
$numExceptions = count($exceptions);
if ($numExceptions !== $numRules - 1) {
throw $this->reportError($input)->setRelated($exceptions);
}
return true;
}
public function validate($input)
{
$rulesPassedCount = 0;
foreach ($this->getRules() as $rule) {
if (!$rule->validate($input)) {
continue;
}
++$rulesPassedCount;
}
return $rulesPassedCount === 1;
}
public function check($input)
{
$exceptions = [];
$rulesPassedCount = 0;
foreach ($this->getRules() as $rule) {
try {
$rule->check($input);
++$rulesPassedCount;
} catch (ValidationException $exception) {
$exceptions[] = $exception;
}
}
if ($rulesPassedCount === 1) {
return true;
}
throw (array_shift($exceptions) ?: $this->reportError($input));
}
}

View file

@ -113,6 +113,7 @@ use Respect\Validation\Rules\Key;
* @method static Validator numericVal()
* @method static Validator objectType()
* @method static Validator odd()
* @method static Validator oneOf(Validatable $v1, Validatable $v_)
* @method static Validator optional(Validatable $rule)
* @method static Validator perfectSquare()
* @method static Validator pesel()

View file

@ -0,0 +1,141 @@
<?php
/*
* This file is part of Respect/Validation.
*
* (c) Alexandre Gomes Gaigalas <alexandre@gaigalas.net>
*
* For the full copyright and license information, please view the "LICENSE.md"
* file that was distributed with this source code.
*/
namespace Respect\Validation\Rules;
/**
* @group rule
* @covers \Respect\Validation\Rules\OneOf
* @covers \Respect\Validation\Exceptions\OneOfException
*/
class OneOfTest extends \PHPUnit_Framework_TestCase
{
public function testValid()
{
$valid1 = new Callback(function () {
return false;
});
$valid2 = new Callback(function () {
return true;
});
$valid3 = new Callback(function () {
return false;
});
$rule = new OneOf($valid1, $valid2, $valid3);
$this->assertTrue($rule->validate('any'));
$this->assertTrue($rule->assert('any'));
$this->assertTrue($rule->check('any'));
}
/**
* @expectedException \Respect\Validation\Exceptions\OneOfException
*/
public function testEmptyChain()
{
$rule = new OneOf();
$this->assertFalse($rule->validate('any'));
$this->assertFalse($rule->check('any'));
}
/**
* @expectedException \Respect\Validation\Exceptions\OneOfException
*/
public function testInvalid()
{
$valid1 = new Callback(function () {
return false;
});
$valid2 = new Callback(function () {
return false;
});
$valid3 = new Callback(function () {
return false;
});
$rule = new OneOf($valid1, $valid2, $valid3);
$this->assertFalse($rule->validate('any'));
$this->assertFalse($rule->assert('any'));
}
/**
* @expectedException \Respect\Validation\Exceptions\OneOfException
*/
public function testInvalidMultipleAssert()
{
$valid1 = new Callback(function () {
return true;
});
$valid2 = new Callback(function () {
return true;
});
$valid3 = new Callback(function () {
return false;
});
$rule = new OneOf($valid1, $valid2, $valid3);
$this->assertFalse($rule->validate('any'));
$rule->assert('any');
}
/**
* @expectedException \Respect\Validation\Exceptions\CallbackException
*/
public function testInvalidMultipleCheck()
{
$valid1 = new Callback(function () {
return true;
});
$valid2 = new Callback(function () {
return true;
});
$valid3 = new Callback(function () {
return false;
});
$rule = new OneOf($valid1, $valid2, $valid3);
$this->assertFalse($rule->validate('any'));
$rule->check('any');
}
/**
* @expectedException \Respect\Validation\Exceptions\OneOfException
*/
public function testInvalidMultipleCheckAllValid()
{
$valid1 = new Callback(function () {
return true;
});
$valid2 = new Callback(function () {
return true;
});
$valid3 = new Callback(function () {
return true;
});
$rule = new OneOf($valid1, $valid2, $valid3);
$this->assertFalse($rule->validate('any'));
$rule->check('any');
}
/**
* @expectedException \Respect\Validation\Exceptions\XdigitException
*/
public function testInvalidCheck()
{
$rule = new OneOf(new Xdigit(), new Alnum());
$this->assertFalse($rule->validate(-10));
$rule->check(-10);
}
}