Create "Optional" rule

This commit is contained in:
Henrique Moody 2015-10-07 02:41:38 -03:00
parent 3e45647b81
commit 455ff9b318
10 changed files with 322 additions and 0 deletions

28
docs/Optional.md Normal file
View file

@ -0,0 +1,28 @@
# Optional
- `v::optional(v $rule)`
- `v::optional(v $rule, array $optionalValues)`
Validates if the given input is optional or not. By _optional_ you may interpret
as `null` or an empty string (`''`).
```php
v::optional(v::alpha())->validate(''); // true
v::optional(v::digit())->validate(null); // true
```
Also you can defined what values you want as optional values:
```php
v::optional(v::alpha(), array(null))->validate(''); // false
```
The example bellow returns false because only `null` is accepted as optional
value, and `''` is not.
***
See also:
* [NotEmpty](NotEmpty.md)
* [NoWhitespace](NoWhitespace.md)
* [NullValue](NullValue.md)

View file

@ -53,6 +53,29 @@ Note that we used `v::string()` and `v::date()` in the beginning of the validato
Although is not mandatory, it is a good practice to use the type of the
validated object as the first node in the chain.
## Input optional
On oldest versions of Respect\Validation all validators treat input as optional
and accept an empty string input as valid. Even though a useful feature that
caused a lot of troubles for our team and neither was an obvious behavior. Also
there was some people who likes to accept `null` as optional value, not only an
empty string.
For that reason all rules are mandatory now but if you want to treat a value as
optional you can use `v::optional()` rule:
```php
v::alpha()->validate(''); // false input required
v::alpha()->validate(null); // false input required
v::optional(v::alpha())->validate(''); // true
v::optional(v::alpha())->validate(null); // true
```
By _optional_ you may interpret as `null` or an empty string (`''`).
See more on [Optional](Optional.md).
## Negating Rules
You can use the `v::not()` to negate any rule:

View file

@ -28,6 +28,7 @@
* [Callback](Callback.md)
* [FilterVar](FilterVar.md)
* [Not](Not.md)
* [Optional](Optional.md)
* [Type](Type.md)
* [When](When.md)
@ -250,6 +251,7 @@
* [Object](Object.md)
* [Odd](Odd.md)
* [OneOf](OneOf.md)
* [Optional](Optional.md)
* [PerfectSquare](PerfectSquare.md)
* [Phone](Phone.md)
* [Positive](Positive.md)

View file

@ -0,0 +1,33 @@
<?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 OptionalException extends ValidationException
{
const STANDARD = 0;
const NAMED = 1;
public static $defaultTemplates = array(
self::MODE_DEFAULT => array(
self::STANDARD => 'The value must be optional',
self::NAMED => '{{name}} must be optional',
),
self::MODE_NEGATIVE => array(
self::STANDARD => 'The value is required',
self::NAMED => '{{name}} is required',
),
);
public function chooseTemplate()
{
return $this->getName() == '' ? static::STANDARD : static::NAMED;
}
}

View file

@ -0,0 +1,58 @@
<?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\Validatable;
use Respect\Validation\Exceptions\ComponentException;
class Optional extends AbstractWrapper
{
public $optionalValues;
public function __construct(Validatable $rule, array $optionalValues = array(null, ''))
{
$this->validatable = $rule;
$this->optionalValues = $optionalValues;
}
private function isOptional($input)
{
return in_array($input, $this->optionalValues, true);
}
public function assert($input)
{
if ($this->isOptional($input)) {
return true;
}
return parent::assert($input);
}
public function check($input)
{
if ($this->isOptional($input)) {
return true;
}
return parent::check($input);
}
public function validate($input)
{
if ($this->isOptional($input)) {
return true;
}
return parent::validate($input);
}
}

View file

@ -92,6 +92,7 @@ use Respect\Validation\Rules\Key;
* @method static Validator object()
* @method static Validator odd()
* @method static Validator oneOf()
* @method static Validator optional(Validatable $rule, array $optionalValues = array(null, ''))
* @method static Validator perfectSquare()
* @method static Validator phone()
* @method static Validator positive()

View file

@ -0,0 +1,14 @@
--FILE--
<?php
require 'vendor/autoload.php';
use Respect\Validation\Validator as v;
v::optional(v::alpha())->assert('');
v::optional(v::alpha())->assert(null);
?>
===DONE===
--EXPECTF--
===DONE===

View file

@ -0,0 +1,14 @@
--FILE--
<?php
require 'vendor/autoload.php';
use Respect\Validation\Validator as v;
v::optional(v::alpha())->check('');
v::optional(v::alpha())->check(null);
?>
===DONE===
--EXPECTF--
===DONE===

View file

@ -0,0 +1,19 @@
--FILE--
<?php
require 'vendor/autoload.php';
use Respect\Validation\Validator as v;
var_dump(v::alpha()->validate(''));
var_dump(v::alpha()->validate(null));
var_dump(v::optional(v::alpha())->validate(''));
var_dump(v::optional(v::alpha())->validate(null));
?>
--EXPECTF--
bool(false)
bool(false)
bool(true)
bool(true)

View file

@ -0,0 +1,130 @@
<?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\Optional
* @covers Respect\Validation\Exceptions\OptionalException
*/
class OptionalTest extends \PHPUnit_Framework_TestCase
{
public function testShouldAcceptInstanceOfValidatobleOnConstructor()
{
$validatable = $this->getMock('Respect\\Validation\\Validatable');
$rule = new Optional($validatable);
$this->assertSame($validatable, $rule->getValidatable());
}
public function testShouldAcceptOptionalValuesOnConstructor()
{
$validatable = $this->getMock('Respect\\Validation\\Validatable');
$optionalValues = array(null, '', ' ', '0');
$rule = new Optional($validatable, $optionalValues);
$this->assertSame($optionalValues, $rule->optionalValues);
}
public function testShouldHaveDefaultOptionalValues()
{
$validatable = $this->getMock('Respect\\Validation\\Validatable');
$expectedOptionalValues = array(null, '');
$rule = new Optional($validatable);
$this->assertSame($expectedOptionalValues, $rule->optionalValues);
}
public function testShouldNotValidateRuleWhenInputIsOptional()
{
$validatable = $this->getMock('Respect\\Validation\\Validatable');
$validatable
->expects($this->never())
->method('validate');
$rule = new Optional($validatable);
$this->assertTrue($rule->validate(''));
}
public function testShouldValidateRuleWhenInputIsNotOptional()
{
$input = 'foo';
$validatable = $this->getMock('Respect\\Validation\\Validatable');
$validatable
->expects($this->once())
->method('validate')
->with($input)
->will($this->returnValue(true));
$rule = new Optional($validatable);
$this->assertTrue($rule->validate($input));
}
public function testShouldNotAssertRuleWhenInputIsOptional()
{
$validatable = $this->getMock('Respect\\Validation\\Validatable');
$validatable
->expects($this->never())
->method('assert');
$rule = new Optional($validatable);
$this->assertTrue($rule->assert(''));
}
public function testShouldAssertRuleWhenInputIsNotOptional()
{
$input = 'foo';
$validatable = $this->getMock('Respect\\Validation\\Validatable');
$validatable
->expects($this->once())
->method('assert')
->with($input)
->will($this->returnValue(true));
$rule = new Optional($validatable);
$this->assertTrue($rule->assert($input));
}
public function testShouldNotCheckRuleWhenInputIsOptional()
{
$validatable = $this->getMock('Respect\\Validation\\Validatable');
$validatable
->expects($this->never())
->method('check');
$rule = new Optional($validatable);
$this->assertTrue($rule->check(''));
}
public function testShouldCheckRuleWhenInputIsNotOptional()
{
$input = 'foo';
$validatable = $this->getMock('Respect\\Validation\\Validatable');
$validatable
->expects($this->once())
->method('check')
->with($input)
->will($this->returnValue(true));
$rule = new Optional($validatable);
$this->assertTrue($rule->check($input));
}
}