Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Extract constraints form Doctrine 2 entity in symfony 2

To keep the field level constraints at a central place (not replicate it in each form), I added the constraints in the entity. Like below (lets say its one of the fields of a user entity):

 /**
 * @var string
 *
 * @ORM\Column(name="email", type="string", length=255, nullable=false)
 *
 * @Constraints\NotBlank(
 *      groups={"register", "edit"},
 *      message="email cannot be blank."
 * )
 * @Constraints\Email(
 *      groups={"register", "edit"},
 *      message="Please enter a valid email address."
 * )
 *
 * @Expose
 * @Groups({"list", "details"})
 */
private $email;

Now I need a way to expose this validation constraints for each field which is an annotation of "Symfony\Component\Validator\Constraints". Is there a way that I can get all the constraints for all fields in the entity, like:

$em->getValidationConstraints('MyBundle:EntityUser'); //em is the entity manager
//and it returns me all the fields with its name, type and any constraints  
//attached to it as any array

Thanks in advance.

like image 932
Geshan Avatar asked Jun 19 '13 06:06

Geshan


1 Answers

Gather Information

Before fixing a problem, it's good to know what you are talking about and gather some information.

Doctrine is an ORM, something that does nice things between a database and an object. It has nothing to do with validation, that is done by the Symfony2 Validator Component. So you need something else than the $em.

All constraints of a class are called 'metadata' and they are usually stored in Symfony\Component\Validator\Mapping\ClassMetadata. We have to find a class which accepts the name of a class and returns a ClassMetadata instance.

To load the constraints, the Symfony2 Validator component uses loaders.

The Solution

We can see that there is a Symfony\Component\Validator\Mapping\ClassMetadataFactory. A factory is always used to build a class from a specific argument. In this case, we know it will create a ClassMetadata and we can see that it accepts a classname. We have to call ClassMetadataFactory::getMetadataFor.

But we see it needs some loaders. We aren't going to do the big job of initializing this factory, what about using the service container? We can see that the container has a validator.mapping.class_metadata_factory service, which is exactly the class we need.

Now we have all of that, let's use it:

// ... in a controller (maybe a seperated class is beter...)
public function someAction()
{
    $metadataFactory = $this->get('validator.mapping.class_metadata_factory');
    $metadata = $metadataFactory->getMetadataFor('Acme\DemoBundle\Entity\EntityUser');
}

Now we have the metadata, we only need to convert that to an array:

// ...
$propertiesMetadata = $metadata->properties;
$constraints = array();

foreach ($propertiesMetadata as $propertyMetadata) {
    $constraints[$propertyMetadata->name] = $property->constraints;
}

Now, $constraints is an array with all fields and their constraint data, something like:

Array (
    ...

    [email] => Array (
        [0] => <instance of NotBlank>
        [1] => <instance of Email>
    ),
)
like image 68
Wouter J Avatar answered Nov 04 '22 15:11

Wouter J