diff --git a/src/Symfony/Component/Serializer/NameConverter/CamelCaseToSnakeCaseNameConverter.php b/src/Symfony/Component/Serializer/NameConverter/CamelCaseToSnakeCaseNameConverter.php index 861c37b349..508881e84c 100644 --- a/src/Symfony/Component/Serializer/NameConverter/CamelCaseToSnakeCaseNameConverter.php +++ b/src/Symfony/Component/Serializer/NameConverter/CamelCaseToSnakeCaseNameConverter.php @@ -76,7 +76,7 @@ class CamelCaseToSnakeCaseNameConverter implements NameConverterInterface } if (null === $this->attributes || in_array($camelCasedName, $this->attributes)) { - return $this->lowerCamelCase ? lcfirst($camelCasedName) : $camelCasedName; + return $camelCasedName; } return $propertyName; diff --git a/src/Symfony/Component/Validator/Mapping/ClassMetadata.php b/src/Symfony/Component/Validator/Mapping/ClassMetadata.php index 9c305e998c..fe5e6e51e2 100644 --- a/src/Symfony/Component/Validator/Mapping/ClassMetadata.php +++ b/src/Symfony/Component/Validator/Mapping/ClassMetadata.php @@ -299,8 +299,9 @@ class ClassMetadata extends ElementMetadata implements ClassMetadataInterface * The name of the getter is assumed to be the name of the property with an * uppercased first letter and either the prefix "get" or "is". * - * @param string $property The name of the property - * @param Constraint $constraint The constraint + * @param string $property The name of the property + * @param Constraint $constraint The constraint + * @param string|null $method The method that is called to retrieve the value being validated (null for auto-detection) * * @return $this */ @@ -319,6 +320,30 @@ class ClassMetadata extends ElementMetadata implements ClassMetadataInterface return $this; } + /** + * Adds a constraint to the getter of the given property. + * + * @param string $property The name of the property + * @param string $method The name of the getter method + * @param Constraint $constraint The constraint + * + * @return $this + */ + public function addGetterMethodConstraint($property, $method, Constraint $constraint) + { + if (!isset($this->getters[$property])) { + $this->getters[$property] = new GetterMetadata($this->getClassName(), $property, $method); + + $this->addPropertyMetadata($this->getters[$property]); + } + + $constraint->addImplicitGroupName($this->getDefaultGroup()); + + $this->getters[$property]->addConstraint($constraint); + + return $this; + } + /** * @param string $property * @param Constraint[] $constraints @@ -334,6 +359,22 @@ class ClassMetadata extends ElementMetadata implements ClassMetadataInterface return $this; } + /** + * @param string $property + * @param string $method + * @param Constraint[] $constraints + * + * @return $this + */ + public function addGetterMethodConstraints($property, $method, array $constraints) + { + foreach ($constraints as $constraint) { + $this->addGetterMethodConstraint($property, $method, $constraint); + } + + return $this; + } + /** * Merges the constraints of the given metadata into this object. * diff --git a/src/Symfony/Component/Validator/Mapping/GetterMetadata.php b/src/Symfony/Component/Validator/Mapping/GetterMetadata.php index 0cf9992769..cd42c4338c 100644 --- a/src/Symfony/Component/Validator/Mapping/GetterMetadata.php +++ b/src/Symfony/Component/Validator/Mapping/GetterMetadata.php @@ -35,25 +35,30 @@ class GetterMetadata extends MemberMetadata /** * Constructor. * - * @param string $class The class the getter is defined on - * @param string $property The property which the getter returns + * @param string $class The class the getter is defined on + * @param string $property The property which the getter returns + * @param string|null $method The method that is called to retrieve the value being validated (null for auto-detection) * * @throws ValidatorException */ - public function __construct($class, $property) + public function __construct($class, $property, $method = null) { - $getMethod = 'get'.ucfirst($property); - $isMethod = 'is'.ucfirst($property); - $hasMethod = 'has'.ucfirst($property); + if (null === $method) { + $getMethod = 'get'.ucfirst($property); + $isMethod = 'is'.ucfirst($property); + $hasMethod = 'has'.ucfirst($property); - if (method_exists($class, $getMethod)) { - $method = $getMethod; - } elseif (method_exists($class, $isMethod)) { - $method = $isMethod; - } elseif (method_exists($class, $hasMethod)) { - $method = $hasMethod; - } else { - throw new ValidatorException(sprintf('Neither of these methods exist in class %s: %s, %s, %s', $class, $getMethod, $isMethod, $hasMethod)); + if (method_exists($class, $getMethod)) { + $method = $getMethod; + } elseif (method_exists($class, $isMethod)) { + $method = $isMethod; + } elseif (method_exists($class, $hasMethod)) { + $method = $hasMethod; + } else { + throw new ValidatorException(sprintf('Neither of these methods exist in class %s: %s, %s, %s', $class, $getMethod, $isMethod, $hasMethod)); + } + } elseif (!method_exists($class, $method)) { + throw new ValidatorException(sprintf('The %s() method does not exist in class %s.', $method, $class)); } parent::__construct($class, $method, $property); diff --git a/src/Symfony/Component/Validator/Mapping/Loader/AnnotationLoader.php b/src/Symfony/Component/Validator/Mapping/Loader/AnnotationLoader.php index d1b8c35b36..a3b5f7ee8e 100644 --- a/src/Symfony/Component/Validator/Mapping/Loader/AnnotationLoader.php +++ b/src/Symfony/Component/Validator/Mapping/Loader/AnnotationLoader.php @@ -79,7 +79,7 @@ class AnnotationLoader implements LoaderInterface $metadata->addConstraint($constraint); } elseif ($constraint instanceof Constraint) { if (preg_match('/^(get|is|has)(.+)$/i', $method->name, $matches)) { - $metadata->addGetterConstraint(lcfirst($matches[2]), $constraint); + $metadata->addGetterMethodConstraint(lcfirst($matches[2]), $matches[0], $constraint); } else { throw new MappingException(sprintf('The constraint on "%s::%s" cannot be added. Constraints can only be added on methods beginning with "get", "is" or "has".', $className, $method->name)); } diff --git a/src/Symfony/Component/Validator/Tests/Fixtures/Entity.php b/src/Symfony/Component/Validator/Tests/Fixtures/Entity.php index 77a86108a1..e30619103d 100644 --- a/src/Symfony/Component/Validator/Tests/Fixtures/Entity.php +++ b/src/Symfony/Component/Validator/Tests/Fixtures/Entity.php @@ -63,6 +63,10 @@ class Entity extends EntityParent implements EntityInterfaceB return $this->lastName; } + public function getValid() + { + } + /** * @Assert\IsTrue */ diff --git a/src/Symfony/Component/Validator/Tests/Mapping/GetterMetadataTest.php b/src/Symfony/Component/Validator/Tests/Mapping/GetterMetadataTest.php index 2c21d09487..682ef94cb4 100644 --- a/src/Symfony/Component/Validator/Tests/Mapping/GetterMetadataTest.php +++ b/src/Symfony/Component/Validator/Tests/Mapping/GetterMetadataTest.php @@ -48,7 +48,7 @@ class GetterMetadataTest extends TestCase public function testGetPropertyValueFromIsser() { $entity = new Entity(); - $metadata = new GetterMetadata(self::CLASSNAME, 'valid'); + $metadata = new GetterMetadata(self::CLASSNAME, 'valid', 'isValid'); $this->assertEquals('valid', $metadata->getPropertyValue($entity)); } @@ -60,4 +60,13 @@ class GetterMetadataTest extends TestCase $this->assertEquals('permissions', $metadata->getPropertyValue($entity)); } + + /** + * @expectedException \Symfony\Component\Validator\Exception\ValidatorException + * @expectedExceptionMessage The hasLastName() method does not exist in class Symfony\Component\Validator\Tests\Fixtures\Entity. + */ + public function testUndefinedMethodNameThrowsException() + { + new GetterMetadata(self::CLASSNAME, 'lastName', 'hasLastName'); + } } diff --git a/src/Symfony/Component/Validator/Tests/Mapping/Loader/AnnotationLoaderTest.php b/src/Symfony/Component/Validator/Tests/Mapping/Loader/AnnotationLoaderTest.php index fb776ae32c..ba70992011 100644 --- a/src/Symfony/Component/Validator/Tests/Mapping/Loader/AnnotationLoaderTest.php +++ b/src/Symfony/Component/Validator/Tests/Mapping/Loader/AnnotationLoaderTest.php @@ -69,7 +69,7 @@ class AnnotationLoaderTest extends TestCase 'choices' => array('A', 'B'), ))); $expected->addGetterConstraint('lastName', new NotNull()); - $expected->addGetterConstraint('valid', new IsTrue()); + $expected->addGetterMethodConstraint('valid', 'isValid', new IsTrue()); $expected->addGetterConstraint('permissions', new IsTrue()); // load reflection class so that the comparison passes @@ -139,7 +139,7 @@ class AnnotationLoaderTest extends TestCase 'choices' => array('A', 'B'), ))); $expected->addGetterConstraint('lastName', new NotNull()); - $expected->addGetterConstraint('valid', new IsTrue()); + $expected->addGetterMethodConstraint('valid', 'isValid', new IsTrue()); $expected->addGetterConstraint('permissions', new IsTrue()); // load reflection class so that the comparison passes