Fix Symfony validation rule.

The API of Symfony\Component\Validator changed more than 2 years ago, so
this validation rule wasn't working for quite a long time.

This fixes the validator to work with versions >= 2.1 of
Symfony/Validation as the change on composer shows us. Although a bug
fix, this breaks compatibility with people already using this validator.

I was astonished to not find any tests for that validator also. :(
With those tests we can ensure that any change on the component API will
be noticed by us.
This commit is contained in:
Augusto Pascutti 2014-02-16 02:59:24 -03:00
parent d1a2f18a80
commit c2850055bc
3 changed files with 117 additions and 25 deletions

View file

@ -17,7 +17,7 @@
}],
"require-dev": {
"zendframework/zend-validator": "2.*",
"symfony/validator": "2.*",
"symfony/validator": ">=2.1.0",
"phpunit/phpunit": "3.7.*"
},
"suggest": {

View file

@ -2,51 +2,65 @@
namespace Respect\Validation\Rules;
use ReflectionClass;
use ReflectionException;
use Respect\Validation\Exceptions\ComponentException;
use Symfony\Component\Validator\Validation;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintViolation;
class Sf extends AbstractRule
{
const SYMFONY_CONSTRAINT_NAMESPACE = 'Symfony\Component\Validator\Constraints\%s';
public $name;
protected $constraint;
protected $messages = array();
protected $validator;
private $constraint;
public function __construct($name, $params=array())
{
$this->name = ucfirst($name);
$sfMirrorConstraint = new ReflectionClass(
'Symfony\Component\Validator\Constraints\\' . $this->name
);
if ($sfMirrorConstraint->hasMethod('__construct')) {
$this->constraint = $sfMirrorConstraint->newInstanceArgs($params);
} else {
$this->constraint = $sfMirrorConstraint->newInstance();
$this->constraint = $this->createSymfonyConstraint($this->name, $params);
}
private function createSymfonyConstraint($constraintName, $constraintConstructorParameters=array())
{
$fullClassName = sprintf(self::SYMFONY_CONSTRAINT_NAMESPACE, $constraintName);
try {
$constraintReflection = new ReflectionClass($fullClassName);
} catch (ReflectionException $previousException) {
$baseExceptionMessage = 'Symfony/Validator constraint "%s" does not exist.';
$exceptionMessage = sprintf($baseExceptionMessage, $constraintName);
throw new ComponentException($exceptionMessage, 0, $previousException);
}
if ($constraintReflection->hasMethod('__construct')) {
return $constraintReflection->newInstanceArgs($constraintConstructorParameters);
}
return $constraintReflection->newInstance();
}
private function returnViolationsForConstraint($valueToValidate, Constraint $symfonyConstraint)
{
$validator = Validation::createValidator(); // You gotta love those Symfony namings
return $validator->validateValue($valueToValidate, $symfonyConstraint);
}
public function assert($input)
{
if (!$this->validate($input)) {
$violation = new ConstraintViolation(
$this->validator->getMessageTemplate(),
$this->validator->getMessageParameters(),
'',
'',
$input
);
throw $this->reportError($violation->getMessage());
$violations = $this->returnViolationsForConstraint($input, $this->constraint);
if (count($violations) == 0) {
return true;
}
return true;
throw $this->reportError((string) $violations);
}
public function validate($input)
{
$validatorName = 'Symfony\Component\Validator\Constraints\\'
. $this->name . 'Validator';
$this->validator = new $validatorName;
$violations = $this->returnViolationsForConstraint($input, $this->constraint);
if (count($violations)) {
return false;
}
return $this->validator->isValid($input, $this->constraint);
return true;
}
}

View file

@ -0,0 +1,78 @@
<?php
namespace Respect\Validation\Rules;
use Respect\Validation\Validator as v;
class SfTest extends \PHPUnit_Framework_TestCase
{
public function assertPreConditions()
{
if (false === class_exists('Symfony\Component\Validator\Constraints\Time')) {
$this->markTestSkipped('Expected Symfony\Validator installed.');
}
}
public function testValidationWithAnExistingValidationConstraint()
{
$constraintName = 'Time';
$validConstraintValue = '04:20:00';
$invalidConstraintValue = 'yada';
$this->assertTrue(
v::sf($constraintName)->validate($validConstraintValue),
sprintf('"%s" should be valid under "%s" constraint.', $validConstraintValue, $constraintName)
);
$this->assertFalse(
v::sf($constraintName)->validate($invalidConstraintValue),
sprintf('"%s" should be invalid under "%s" constraint.', $invalidConstraintValue, $constraintName)
);
}
/**
* @depends testValidationWithAnExistingValidationConstraint
*/
public function testAssertionWithAnExistingValidationConstraint()
{
$constraintName = 'Time';
$validConstraintValue = '04:20:00';
$this->assertTrue(
v::sf($constraintName)->assert($validConstraintValue),
sprintf('"%s" should be valid under "%s" constraint.', $validConstraintValue, $constraintName)
);
}
/**
* @depends testAssertionWithAnExistingValidationConstraint
*/
public function testAssertionMessageWithAnExistingValidationConstraint()
{
$constraintName = 'Time';
$invalidConstraintValue = '34:90:70';
try {
v::sf($constraintName)->assert($invalidConstraintValue);
} catch (\Respect\Validation\Exceptions\AllOfException $exception) {
$fullValidationMessage = $exception->getFullMessage();
$expectedValidationException = <<<EOF
\-These rules must pass for "34:90:70"
\-Time
EOF;
return $this->assertEquals(
$expectedValidationException,
$fullValidationMessage,
'Exception message is different from the one expected.'
);
}
$this->fail('Validation exception expected to compare message.');
}
/**
* @expectedException Respect\Validation\Exceptions\ComponentException
* @expectedExceptionMessage Symfony/Validator constraint "FluxCapacitor" does not exist.
*/
public function testValidationWithNonExistingConstraint()
{
$fantasyConstraintName = 'FluxCapacitor';
$fantasyValue = '8GW';
v::sf($fantasyConstraintName)->validate($fantasyValue);
}
}