Symonfy2 validation: defining constraints in yml and validating an array

All I'm trying to do is:

  • define limitations in yml

  • use this to check array

Say an array of products:

$product['name'] = 'A book'; $product['date'] = '2012-09'; $product['price'] = '21.5'; 

How to do it?

+7
source share
3 answers

First of all, you need to know that Symfony2 validators are not ready for this easily. It took me some time and some reading of the source of Symfony2 to get a working solution for your case, and my solution is not so natural.

I created a class that accepts a validator, your array and your yaml configuration file so you can do what you expect. This class extends Symfony’s YamlFileLoader to access the parseNodes protected method: this isn’t beautiful, but the only way I have found converting a custom Yaml configuration file to an array of Constraint objects.

So here we are. I give you my code, you will need to replace some namespaces according to your own context.

First create a controller for our demo:

  public function indexAction() { // We create a sample validation file for the demo $demo = <<< EOT name: - NotBlank: ~ - MinLength: { limit: 3 } - MaxLength: { limit: 10 } date: - NotBlank: ~ - Regex: "/^[0-9]{4}\-[0-9]{2}$/" price: - Min: 0 EOT; file_put_contents("/tmp/test.yml", $demo); // We create your array to validate $product = array (); $product['name'] = 'A book'; $product['date'] = '2012-09'; $product['price'] = '21.5'; $validator = $this->get('validator'); $service = new \Fuz\TestsBundle\Services\ArrayValidator($validator, $product, "/tmp/test.yml"); $errors = $service->validate(); echo '<pre>'; var_dump($errors); die(); return $this->render('FuzTestsBundle:Default:index.html.twig'); } 

Then create a class called ArrayValidator.php. Again, take care of the namespace.

 <?php namespace Fuz\TestsBundle\Services; use Symfony\Component\Validator\ValidatorInterface; use Symfony\Component\Yaml\Parser; use Symfony\Component\Validator\Mapping\Loader\YamlFileLoader; /** * This class inherits from YamlFileLoader because we need to call the * parseNodes() protected method. */ class ArrayValidator extends YamlFileLoader { /* the @validator service */ private $validator; /* The array to check */ private $array; /* The file that contains your validation rules */ private $validationFile; public function __construct(ValidatorInterface $validator, array $array = array(), $validationFile) { $this->validator = $validator; $this->array = $array; $this->validationFile = $validationFile; } /* The method that does what you want */ public function validate() { $yaml = file_get_contents($this->validationFile); // We parse the yaml validation file $parser = new Parser(); $parsedYaml = $parser->parse($yaml); // We transform this validation array to a Constraint array $arrayConstraints = $this->parseNodes($parsedYaml); // For each elements of the array, we execute the validation $errors = array(); foreach ($this->array as $key => $value) { $errors[$key] = array(); // If the array key (eg: price) has validation rules, we check the value if (isset($arrayConstraints[$key])) { foreach ($arrayConstraints[$key] as $constraint) { // If there is constraint violations, we list messages $violationList = $this->validator->validateValue($value, $constraint); if (count($violationList) > 0) { foreach ($violationList as $violation) { $errors[$key][] = $violation->getMessage(); } } } } } return $errors; } } 

Finally, check it with different values ​​in the $ product array.

Default:

  $product = array (); $product['name'] = 'A book'; $product['date'] = '2012-09'; $product['price'] = '21.5'; 

The following is displayed:

 array(3) { ["name"]=> array(0) { } ["date"]=> array(0) { } ["price"]=> array(0) { } } 

If we change the values ​​to:

  $product = array (); $product['name'] = 'A very interesting book'; $product['date'] = '2012-09-03'; $product['price'] = '-21.5'; 

You'll get:

 array(3) { ["name"]=> array(1) { [0]=> string(61) "This value is too long. It should have 10 characters or less." } ["date"]=> array(1) { [0]=> string(24) "This value is not valid." } ["price"]=> array(1) { [0]=> string(31) "This value should be 0 or more." } } 

Hope this helps.

+4
source

it’s easy to check how the array is checked, I found it in the silex documentation

 use Symfony\Component\Validator\Constraints as Assert; ... ... $constraint = new Assert\Collection(array( 'Name' => new Assert\MinLength(10), 'author' => new Assert\Collection(array( 'first_name' => array(new Assert\NotBlank(), new Assert\MinLength(10)), 'last_name' => new Assert\MinLength(10), )), )); $errors = $this->get('validator')->validateValue($book, $constraint); 

or you can create direct forms with restrictions

 $form = $this->get('form.factory')->createBuilder('form',array(),array( 'csrf_protection' => false, 'validation_constraint' => new Assert\Collection(array( 'name' => new Assert\NotBlank(array( 'message' => 'Can\'t be null' )), 'email' => new Assert\Email(array( 'message' => 'Invalid email' )), )) )) ->add('name', 'text') ->add('email', 'email') ->getForm(); } 

this code may solve your second point, but for the first point, I suggest you write your own class that converts your yaml definition into a valid array of constraints with instances of validation constraint objects, or maybe it gives the form itself!

I don't know a class ready to do this in symfony2.

I did this in other projects that do not have a good data model, but in symfony you can create your own models and define the validations associated with it.

+1
source

in your validation.yml:

 Acme\DemoBundle\Entity\AcmeEntity: properties: price: - NotBlank: ~ - Acme\DemoBundle\Validator\Constraints\ContainsAlphanumeric: ~ 

and your ContainsAlphanumeric:

 <?php namespace Acme\DemoBundle\Validator\Constraints; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\ConstraintValidator; class ContainsAlphanumericValidator extends ConstraintValidator { public function validate($value, Constraint $constraint) { if (!preg_match('/^[a-zA-Za0-9]+$/', $value, $matches)) { $this->context->addViolation($constraint->message, array('%string%' => $value)); } } } ?> 
-one
source

All Articles