Skip to content

Commit

Permalink
Fix Symfony validation rule.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Augusto Pascutti committed Feb 16, 2014
1 parent d1a2f18 commit c285005
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 25 deletions.
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
}],
"require-dev": {
"zendframework/zend-validator": "2.*",
"symfony/validator": "2.*",
"symfony/validator": ">=2.1.0",
"phpunit/phpunit": "3.7.*"
},
"suggest": {
Expand Down
62 changes: 38 additions & 24 deletions library/Respect/Validation/Rules/Sf.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}

78 changes: 78 additions & 0 deletions tests/library/Respect/Validation/Rules/SfTest.php
Original file line number Diff line number Diff line change
@@ -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);
}
}

0 comments on commit c285005

Please sign in to comment.