Issue #260: Now using Respect\Validation exceptions only

* `ExceptionInterface`: all Respect\Validation\Exceptions implement through inheritance
* `ValidatorExceptionInterface`: implemented only by AllOfException.  This allows the end users to know when there has been a Validator exception rather than just any of our exceptions.

Fixed formatting issues

Created two new exception types

* Created ValidationExceptionInterface
* Created NestedValidationExceptionInterface which extends ValidationExceptionInterface
    * Renamed from ValidatorExceptionInterface

* ValidationException implements ValidationExceptionInterface and ValidationExceptionTest checks for the implementation.
* AbstractNestedException implements NestedValidationExceptionInterface and AbstractNestedExceptionTest checks for the implementation.

* CheckExceptionsTest now checks all Rule exceptions to make sure they implement ValidationExceptionInterface
* ValidatorTest now contains test that shows that only ValidationExceptionInterface can be used reliably with `::check()`

* Updated documentation for new exception types
* Reworked examples to show how to catch the exception interfaces

Minor changes in readme.md and ExceptionInterfaces

* Removed `import` statements (hahaha)
* Renamed `$e` to `$exception`

* `ValidationExceptionInterface` now extends `ExceptionInterface`.  Changed `ValidationException` to match
This commit is contained in:
Andy Wendt 2015-01-29 18:53:20 -07:00
parent 116be2bbef
commit ab65035181
11 changed files with 132 additions and 23 deletions

View file

@ -108,16 +108,30 @@ $usernameValidator->validate('alexandre gaigalas'); //false
$usernameValidator->validate('#$%'); //false
```
### Exception Types
* `Respect\Validation\Exceptions\NestedValidationExceptionInterface`:
* Use when calling `assert()`.
* Interface has three methods: `getFullMessage()`, `findMessages()`, and `getMainMessage()`.
* `Respect\Validation\Exceptions\ValidationExceptionInterface`:
* Use when calling `::check()`.
* All `Respect\Validation` validation exceptions implement this interface.
* Interface has one method: `getMainMessage()`;
* `Repect\Validation\Exceptions\ExceptionInterface`:
* All `Respect\Validation\Exceptions` implement this interface.
### Informative Exceptions
When something goes wrong, Validation can tell you exactly what's going on. For this,
we use the `assert()` method instead of `validate()`:
```php
use Respect\Validation\Exceptions\NestedValidationExceptionInterface
try {
$usernameValidator->assert('really messed up screen#name');
} catch(DomainException $e) {
echo $e->getFullMessage();
} catch(NestedValidationExceptionInterface $exception) {
echo $exception->getFullMessage();
}
```
@ -134,10 +148,12 @@ The text tree is fine, but unusable on a HTML form or something more custom. You
`findMessages()` for that:
```php
use Respect\Validation\Exceptions\NestedValidationExceptionInterface
try {
$usernameValidator->assert('really messed up screen#name');
} catch(\InvalidArgumentException $e) {
var_dump($e->findMessages(array('alnum', 'length', 'noWhitespace')));
} catch(NestedValidationExceptionInterface $exception) {
var_dump($exception->findMessages(array('alnum', 'length', 'noWhitespace')));
}
```
@ -149,7 +165,7 @@ Getting messages as an array is fine, but sometimes you need to customize them i
to present them to the user. This is possible using the `findMessages()` method as well:
```php
$errors = $e->findMessages(array(
$errors = $exception->findMessages(array(
'alnum' => '{{name}} must contain only letters and digits',
'length' => '{{name}} must not have more than 15 chars',
'noWhitespace' => '{{name}} cannot contain spaces'
@ -183,10 +199,12 @@ validation report. There is also a `check()` method that returns an Exception
only with the first error found:
```php
use Respect\Validation\Exceptions\ValidationExceptionInterface
try {
$usernameValidator->check('really messed up screen#name');
} catch(\InvalidArgumentException $e) {
echo $e->getMainMessage();
} catch(ValidationExceptionInterface $exception) {
echo $exception->getMainMessage();
}
```

View file

@ -5,7 +5,7 @@ use RecursiveIteratorIterator;
use RecursiveTreeIterator;
use Respect\Validation\ExceptionIterator;
class AbstractNestedException extends ValidationException
class AbstractNestedException extends ValidationException implements NestedValidationExceptionInterface
{
const ITERATE_TREE = 1;
const ITERATE_ALL = 2;

View file

@ -3,6 +3,6 @@ namespace Respect\Validation\Exceptions;
use Exception;
class ComponentException extends Exception
class ComponentException extends Exception implements ExceptionInterface
{
}

View file

@ -0,0 +1,6 @@
<?php
namespace Respect\Validation\Exceptions;
interface ExceptionInterface
{
}

View file

@ -0,0 +1,8 @@
<?php
namespace Respect\Validation\Exceptions;
interface NestedValidationExceptionInterface extends ValidationExceptionInterface
{
public function findMessages(array $paths);
public function getFullMessage();
}

View file

@ -2,10 +2,9 @@
namespace Respect\Validation\Exceptions;
use DateTime;
use Exception;
use InvalidArgumentException;
class ValidationException extends InvalidArgumentException
class ValidationException extends InvalidArgumentException implements ValidationExceptionInterface
{
const MODE_DEFAULT = 1;
const MODE_NEGATIVE = 2;

View file

@ -0,0 +1,7 @@
<?php
namespace Respect\Validation\Exceptions;
interface ValidationExceptionInterface extends ExceptionInterface
{
public function getMainMessage();
}

View file

@ -5,6 +5,13 @@ use Respect\Validation\Validator as v;
class AbstractNestedExceptionTest extends \PHPUnit_Framework_TestCase
{
public function testItImplementsNestedValidationExceptionInterface()
{
$abstractNestedException = $this->getMock('Respect\Validation\Exceptions\AbstractNestedException');
$this->assertInstanceOf('Respect\Validation\Exceptions\NestedValidationExceptionInterface',
$abstractNestedException);
}
public function testGetRelatedShouldReturnExceptionAddedByAddRelated()
{
$composite = new AttributeException;
@ -75,7 +82,7 @@ class AbstractNestedExceptionTest extends \PHPUnit_Framework_TestCase
'security_question' => null,
)
);
} catch (ValidationException $e) {
} catch (NestedValidationExceptionInterface $e) {
$messages = $e->findMessages(
array('allOf', 'first_name.length')
);
@ -114,7 +121,7 @@ class AbstractNestedExceptionTest extends \PHPUnit_Framework_TestCase
'security_question' => null,
)
);
} catch (ValidationException $e) {
} catch (NestedValidationExceptionInterface $e) {
$messages = $e->findMessages(
array(
'allOf' => 'Invalid {{name}}',
@ -127,4 +134,3 @@ class AbstractNestedExceptionTest extends \PHPUnit_Framework_TestCase
}
}
}

View file

@ -47,7 +47,7 @@ class CheckExceptionsTest extends \PHPUnit_Framework_TestCase
$missingExceptions = array();
foreach ($this->getAllRuleNames() as $ruleName) {
$exceptionClass = 'Respect\\Validation\\Exceptions\\'.$ruleName.'Exception';
$exceptionClass = $this->buildExceptionClass($ruleName);
if (class_exists($exceptionClass)) {
continue;
}
@ -55,6 +55,44 @@ class CheckExceptionsTest extends \PHPUnit_Framework_TestCase
$missingExceptions[] = $ruleName;
}
$this->assertEmpty($missingExceptions, 'No exceptions for: '.implode(', ', $missingExceptions));
$this->assertEmpty($missingExceptions, 'No exceptions for: ' . $this->formatArrayAsString($missingExceptions));
}
public function testEveryRuleExceptionImplementsValidationExceptionInterface()
{
$exceptionsNotImplementingInterface = array();
foreach ($this->getAllRuleNames() as $ruleName) {
$exceptionClass = $this->buildExceptionClass($ruleName);
$exceptionClassMock = $this->getMock($exceptionClass);
if ($exceptionClassMock instanceof ValidationExceptionInterface) {
continue;
}
$exceptionsNotImplementingInterface[] = $ruleName;
}
$this->assertEmpty($exceptionsNotImplementingInterface,
'ValidationExceptionInterface not implemented in: ' .
$this->formatArrayAsString($exceptionsNotImplementingInterface));
}
/**
* @param string $ruleName
* @return string
*/
private function buildExceptionClass($ruleName)
{
$exceptionClass = 'Respect\\Validation\\Exceptions\\' . $ruleName . 'Exception';
return $exceptionClass;
}
/**
* @param array $array
* @return string
*/
private function formatArrayAsString(array $array)
{
return implode(', ', $array);
}
}

View file

@ -3,6 +3,19 @@ namespace Respect\Validation\Exceptions;
class ValidationExceptionTest extends \PHPUnit_Framework_TestCase
{
public function testItImplementsValidationExceptionInterface()
{
$validationException = new ValidationException();
$this->assertInstanceOf('Respect\Validation\Exceptions\ValidationExceptionInterface', $validationException);
}
public function testItDoesNotImplementNestedValidationExceptionInterface()
{
$validationException = new ValidationException();
$this->assertNotInstanceOf('Respect\Validation\Exceptions\NestedValidationExceptionInterface',
$validationException);
}
/**
* @dataProvider providerForFormat
*/
@ -98,4 +111,3 @@ class ValidationExceptionTest extends \PHPUnit_Framework_TestCase
);
}
}

View file

@ -1,6 +1,9 @@
<?php
namespace Respect\Validation;
use Respect\Validation\Exceptions\NestedValidationExceptionInterface;
use Respect\Validation\Exceptions\ValidationExceptionInterface;
class ValidatorTest extends \PHPUnit_Framework_TestCase
{
public function testStaticCreateShouldReturnNewValidator()
@ -17,7 +20,7 @@ class ValidatorTest extends \PHPUnit_Framework_TestCase
{
try {
Validator::callback('is_int')->setTemplate('{{name}} is not tasty')->assert('something');
} catch (\Exception $e) {
} catch (NestedValidationExceptionInterface $e) {
$this->assertEquals('"something" is not tasty', $e->getMainMessage());
}
}
@ -25,7 +28,7 @@ class ValidatorTest extends \PHPUnit_Framework_TestCase
{
try {
Validator::callback('is_int')->between(1,2)->setTemplate('{{name}} is not tasty')->assert('something');
} catch (\Exception $e) {
} catch (NestedValidationExceptionInterface $e) {
$this->assertEquals('"something" is not tasty', $e->getMainMessage());
}
}
@ -33,7 +36,7 @@ class ValidatorTest extends \PHPUnit_Framework_TestCase
{
try {
Validator::callback('is_string')->between(1,2)->setTemplate('{{name}} is not tasty')->assert('something');
} catch (\Exception $e) {
} catch (NestedValidationExceptionInterface $e) {
$this->assertEquals('\-"something" is not tasty
\-"something" must be greater than 1', $e->getFullMessage());
}
@ -42,7 +45,7 @@ class ValidatorTest extends \PHPUnit_Framework_TestCase
{
try {
Validator::string()->length(1,15)->assert('');
} catch (\Exception $e) {
} catch (NestedValidationExceptionInterface $e) {
$this->assertEquals('\-These rules must pass for ""
\-"" must have a length between 1 and 15', $e->getFullMessage());
}
@ -70,7 +73,7 @@ class ValidatorTest extends \PHPUnit_Framework_TestCase
$usernameValidator = Validator::alnum('_')->length(1,15)->noWhitespace();
try {
$usernameValidator->assert('really messed up screen#name');
} catch (\InvalidArgumentException $e) {
} catch (NestedValidationExceptionInterface $e) {
$e->findMessages(array('alnum', 'length', 'noWhitespace'));
}
}
@ -82,7 +85,7 @@ class ValidatorTest extends \PHPUnit_Framework_TestCase
->key('birthdate', Validator::date())
->setName("User Subscription Form")
->assert(array('username' => '', 'birthdate' => ''));
} catch (\InvalidArgumentException $e) {
} catch (NestedValidationExceptionInterface $e) {
$this->assertEquals('\-These rules must pass for User Subscription Form
|-Key username must be valid
| \-"" must have a length between 1 and 32
@ -110,4 +113,16 @@ class ValidatorTest extends \PHPUnit_Framework_TestCase
$this->assertSame($validator, $validator->not($validator->notEmpty()));
}
public function testDoNotRelyOnNestedValidationExceptionInterfaceForCheck()
{
$usernameValidator = Validator::alnum('_')->length(1, 15)->noWhitespace();
try {
$usernameValidator->check('really messed up screen#name');
} catch (NestedValidationExceptionInterface $e) {
$this->fail('Check used NestedValidationException');
} catch (ValidationExceptionInterface $e) {
$this->assertTrue(true);
}
}
}