mirror of
https://github.com/Respect/Validation.git
synced 2024-05-29 03:42:25 +02:00
Create KeySet rule
This commit is contained in:
parent
29fd82815b
commit
7d9d19009a
50
docs/KeySet.md
Normal file
50
docs/KeySet.md
Normal file
|
@ -0,0 +1,50 @@
|
|||
# KeySet
|
||||
|
||||
- `v::keySet(Key $rule...)`
|
||||
|
||||
Validates a keys in a defined structure.
|
||||
|
||||
```php
|
||||
$dict = array('foo' => 42);
|
||||
|
||||
v::keySet(
|
||||
v::key('foo', v::int())
|
||||
)->validate($dict); //true
|
||||
```
|
||||
|
||||
Extra keys are not allowed:
|
||||
```php
|
||||
$dict = array('foo' => 42, 'bar' => 'String');
|
||||
|
||||
v::keySet(
|
||||
v::key('foo', v::int())
|
||||
)->validate($dict); //false
|
||||
```
|
||||
|
||||
Missing required keys are not allowed:
|
||||
```php
|
||||
$dict = array('foo' => 42, 'bar' => 'String');
|
||||
|
||||
v::keySet(
|
||||
v::key('foo', v::int()),
|
||||
v::key('bar', v::string()),
|
||||
v::key('baz', v::bool())
|
||||
)->validate($dict); //false
|
||||
```
|
||||
|
||||
Missing non-required keys are allowed:
|
||||
```php
|
||||
$dict = array('foo' => 42, 'bar' => 'String');
|
||||
|
||||
v::keySet(
|
||||
v::key('foo', v::int()),
|
||||
v::key('bar', v::string()),
|
||||
v::key('baz', v::bool(), false)
|
||||
)->validate($dict); //true
|
||||
```
|
||||
|
||||
The keys' order is not considered in the validation.
|
||||
|
||||
See also:
|
||||
|
||||
* [Key](Key.md)
|
|
@ -90,6 +90,7 @@
|
|||
* [EndsWith](EndsWith.md)
|
||||
* [In](In.md)
|
||||
* [Key](Key.md)
|
||||
* [KeySet](KeySet.md)
|
||||
* [Length](Length.md)
|
||||
* [NotEmpty](NotEmpty.md)
|
||||
* [StartsWith](StartsWith.md)
|
||||
|
@ -213,6 +214,7 @@
|
|||
* [Ip](Ip.md)
|
||||
* [Json](Json.md)
|
||||
* [Key](Key.md)
|
||||
* [KeySet](KeySet.md)
|
||||
* [LeapDate](LeapDate.md)
|
||||
* [LeapYear](LeapYear.md)
|
||||
* [Length](Length.md)
|
||||
|
|
57
library/Exceptions/KeySetException.php
Normal file
57
library/Exceptions/KeySetException.php
Normal file
|
@ -0,0 +1,57 @@
|
|||
<?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;
|
||||
|
||||
class KeySetException extends AbstractGroupedException
|
||||
{
|
||||
const STRUCTURE = 2;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public static $defaultTemplates = array(
|
||||
self::MODE_DEFAULT => array(
|
||||
self::NONE => 'All of the required rules must pass for {{name}}',
|
||||
self::SOME => 'These rules must pass for {{name}}',
|
||||
self::STRUCTURE => 'Must have keys {{keys}}',
|
||||
),
|
||||
self::MODE_NEGATIVE => array(
|
||||
self::NONE => 'None of these rules must pass for {{name}}',
|
||||
self::SOME => 'These rules must not pass for {{name}}',
|
||||
self::STRUCTURE => 'Must not have keys {{keys}}',
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function chooseTemplate()
|
||||
{
|
||||
if ($this->getParam('keys')) {
|
||||
return static::STRUCTURE;
|
||||
}
|
||||
|
||||
return parent::chooseTemplate();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setParam($name, $value)
|
||||
{
|
||||
if ($name === 'keys') {
|
||||
$value = trim(json_encode($value), '[]');
|
||||
}
|
||||
|
||||
return parent::setParam($name, $value);
|
||||
}
|
||||
}
|
145
library/Rules/KeySet.php
Normal file
145
library/Rules/KeySet.php
Normal file
|
@ -0,0 +1,145 @@
|
|||
<?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\ComponentException;
|
||||
use Respect\Validation\Exceptions\KeySetException;
|
||||
use Respect\Validation\Validatable;
|
||||
|
||||
/**
|
||||
* Validates a keys in a defined structure.
|
||||
*
|
||||
* @author Henrique Moody <henriquemoody@gmail.com>
|
||||
*/
|
||||
class KeySet extends AllOf
|
||||
{
|
||||
/**
|
||||
* @param AllOf $rule
|
||||
*
|
||||
* @return Validatable
|
||||
*/
|
||||
private function filterAllOf(AllOf $rule)
|
||||
{
|
||||
$rules = $rule->getRules();
|
||||
if (count($rules) != 1) {
|
||||
throw new ComponentException('AllOf rule must have only one Key rule');
|
||||
}
|
||||
|
||||
return current($rules);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addRule($rule)
|
||||
{
|
||||
if ($rule instanceof AllOf) {
|
||||
$rule = $this->filterAllOf($rule);
|
||||
}
|
||||
|
||||
if (!$rule instanceof Key) {
|
||||
throw new ComponentException('KeySet rule accepts only Key rules');
|
||||
}
|
||||
|
||||
$this->appendRule($rule);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addRules(array $rules)
|
||||
{
|
||||
foreach ($rules as $rule) {
|
||||
$this->addRule($rule);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getKeys()
|
||||
{
|
||||
$keys = array();
|
||||
foreach ($this->getRules() as $keyRule) {
|
||||
$keys[] = $keyRule->reference;
|
||||
}
|
||||
|
||||
return $keys;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $input
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function hasValidStructure($input)
|
||||
{
|
||||
foreach ($this->getRules() as $keyRule) {
|
||||
if (!array_key_exists($keyRule->reference, $input) && $keyRule->mandatory) {
|
||||
return false;
|
||||
}
|
||||
|
||||
unset($input[$keyRule->reference]);
|
||||
}
|
||||
|
||||
return (count($input) == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws KeySetException
|
||||
*/
|
||||
private function checkKeys($input)
|
||||
{
|
||||
if (!$this->hasValidStructure($input)) {
|
||||
$params = array('keys' => $this->getKeys());
|
||||
$exception = $this->reportError($input, $params);
|
||||
|
||||
throw $exception;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function assert($input)
|
||||
{
|
||||
$this->checkKeys($input);
|
||||
|
||||
return parent::assert($input);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function check($input)
|
||||
{
|
||||
$this->checkKeys($input);
|
||||
|
||||
return parent::check($input);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validate($input)
|
||||
{
|
||||
if (!$this->hasValidStructure($input)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return parent::validate($input);
|
||||
}
|
||||
}
|
|
@ -15,6 +15,7 @@ use ReflectionClass;
|
|||
use Respect\Validation\Exceptions\AllOfException;
|
||||
use Respect\Validation\Exceptions\ComponentException;
|
||||
use Respect\Validation\Rules\AllOf;
|
||||
use Respect\Validation\Rules\Key;
|
||||
|
||||
/**
|
||||
* @method static Validator age(int $minAge = null, int $maxAge = null)
|
||||
|
@ -65,6 +66,7 @@ use Respect\Validation\Rules\AllOf;
|
|||
* @method static Validator ip(mixed $ipOptions = null)
|
||||
* @method static Validator json()
|
||||
* @method static Validator key(string $reference, Validatable $referenceValidator = null, bool $mandatory = true)
|
||||
* @method static Validator keySet(Key $rule...)
|
||||
* @method static Validator leapDate(string $format)
|
||||
* @method static Validator leapYear()
|
||||
* @method static Validator length(int $min = null, int $max = null, bool $inclusive = true)
|
||||
|
|
170
tests/Rules/KeySetTest.php
Normal file
170
tests/Rules/KeySetTest.php
Normal file
|
@ -0,0 +1,170 @@
|
|||
<?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 PHPUnit_Framework_TestCase;
|
||||
|
||||
class KeySetTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testShouldAcceptKeyRule()
|
||||
{
|
||||
$key = new Key('foo', new AlwaysValid(), false);
|
||||
$keySet = new KeySet($key);
|
||||
|
||||
$rules = $keySet->getRules();
|
||||
|
||||
$this->assertSame(current($rules), $key);
|
||||
}
|
||||
|
||||
public function testShouldAcceptAllOfWithOneKeyRule()
|
||||
{
|
||||
$key = new Key('foo', new AlwaysValid(), false);
|
||||
$allOf = new AllOf($key);
|
||||
$keySet = new KeySet($allOf);
|
||||
|
||||
$rules = $keySet->getRules();
|
||||
|
||||
$this->assertSame(current($rules), $key);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Respect\Validation\Exceptions\ComponentException
|
||||
* @expectedExceptionMessage AllOf rule must have only one Key rule
|
||||
*/
|
||||
public function testShouldNotAcceptAllOfWithMoreThanOneKeyRule()
|
||||
{
|
||||
$key1 = new Key('foo', new AlwaysValid(), false);
|
||||
$key2 = new Key('bar', new AlwaysValid(), false);
|
||||
$allOf = new AllOf($key1, $key2);
|
||||
|
||||
new KeySet($allOf);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Respect\Validation\Exceptions\ComponentException
|
||||
* @expectedExceptionMessage KeySet rule accepts only Key rules
|
||||
*/
|
||||
public function testShouldNotAcceptAllOfWithANonKeyRule()
|
||||
{
|
||||
$alwaysValid = new AlwaysValid();
|
||||
$allOf = new AllOf($alwaysValid);
|
||||
|
||||
new KeySet($allOf);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Respect\Validation\Exceptions\ComponentException
|
||||
* @expectedExceptionMessage KeySet rule accepts only Key rules
|
||||
*/
|
||||
public function testShouldNotAcceptANonKeyRule()
|
||||
{
|
||||
$alwaysValid = new AlwaysValid();
|
||||
|
||||
new KeySet($alwaysValid);
|
||||
}
|
||||
|
||||
public function testShouldReturnKeys()
|
||||
{
|
||||
$key1 = new Key('foo', new AlwaysValid(), true);
|
||||
$key2 = new Key('bar', new AlwaysValid(), false);
|
||||
|
||||
$keySet = new KeySet($key1, $key2);
|
||||
|
||||
$this->assertEquals(array('foo', 'bar'), $keySet->getKeys());
|
||||
}
|
||||
|
||||
public function testShouldValidateKeysWhenThereAreMissingRequiredKeys()
|
||||
{
|
||||
$input = array(
|
||||
'foo' => 42,
|
||||
);
|
||||
|
||||
$key1 = new Key('foo', new AlwaysValid(), true);
|
||||
$key2 = new Key('bar', new AlwaysValid(), true);
|
||||
|
||||
$keySet = new KeySet($key1, $key2);
|
||||
|
||||
$this->assertFalse($keySet->validate($input));
|
||||
}
|
||||
|
||||
public function testShouldValidateKeysWhenThereAreMissingNonRequiredKeys()
|
||||
{
|
||||
$input = array(
|
||||
'foo' => 42,
|
||||
);
|
||||
|
||||
$key1 = new Key('foo', new AlwaysValid(), true);
|
||||
$key2 = new Key('bar', new AlwaysValid(), false);
|
||||
|
||||
$keySet = new KeySet($key1, $key2);
|
||||
|
||||
$this->assertTrue($keySet->validate($input));
|
||||
}
|
||||
|
||||
public function testShouldValidateKeysWhenThereAreMoreKeys()
|
||||
{
|
||||
$input = array(
|
||||
'foo' => 42,
|
||||
'bar' => 'String',
|
||||
'baz' => false,
|
||||
);
|
||||
|
||||
$key1 = new Key('foo', new AlwaysValid(), false);
|
||||
$key2 = new Key('bar', new AlwaysValid(), false);
|
||||
|
||||
$keySet = new KeySet($key1, $key2);
|
||||
|
||||
$this->assertFalse($keySet->validate($input));
|
||||
}
|
||||
|
||||
public function testShouldValidateKeysWhenEmpty()
|
||||
{
|
||||
$input = array();
|
||||
|
||||
$key1 = new Key('foo', new AlwaysValid(), true);
|
||||
$key2 = new Key('bar', new AlwaysValid(), true);
|
||||
|
||||
$keySet = new KeySet($key1, $key2);
|
||||
|
||||
$this->assertFalse($keySet->validate($input));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Respect\Validation\Exceptions\KeySetException
|
||||
* @expectedExceptionMessage Must have keys "foo","bar"
|
||||
*/
|
||||
public function testShouldCheckKeys()
|
||||
{
|
||||
$input = array();
|
||||
|
||||
$key1 = new Key('foo', new AlwaysValid(), true);
|
||||
$key2 = new Key('bar', new AlwaysValid(), true);
|
||||
|
||||
$keySet = new KeySet($key1, $key2);
|
||||
$keySet->check($input);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Respect\Validation\Exceptions\KeySetException
|
||||
* @expectedExceptionMessage Must have keys "foo","bar"
|
||||
*/
|
||||
public function testShouldAssertKeys()
|
||||
{
|
||||
$input = array();
|
||||
|
||||
$key1 = new Key('foo', new AlwaysValid(), true);
|
||||
$key2 = new Key('bar', new AlwaysValid(), true);
|
||||
|
||||
$keySet = new KeySet($key1, $key2);
|
||||
$keySet->assert($input);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue