All I am trying to do is:
define constraints in yml
use this to validate an array
Say, a product array:
$product['name'] = 'A book';
$product['date'] = '2012-09';
$product['price'] = '21.5';
How to do that?
First of all, you need to know that Symfony2 validators are not ready to do that easily. It took me some time and some Symfony2 source reading to get a working solution for your case, and my solution is not that natural.
I made a class that takes the validator, your array, and your yaml configuration file so you'll be able to do what you expect. This class extends the YamlFileLoader
from Symfony to access the protected parseNodes
method : this is not beautiful but that's the only way I found to transform a custom Yaml configuration file into an array of Constraint
object.
So here we are. I give you my code, you'll 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 named 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, test it with different values in your $product array.
By default :
$product = array ();
$product['name'] = 'A book';
$product['date'] = '2012-09';
$product['price'] = '21.5';
Will display :
array(3) {
["name"]=>
array(0) {
}
["date"]=>
array(0) {
}
["price"]=>
array(0) {
}
}
If we change 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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With