Symonfy2验证:在yml中定义约束,并验证数组

时间:2012-09-21 09:07:33

标签: php forms validation symfony

我所要做的就是:

  1. 在yml中定义约束

  2. 使用它来验证数组

  3. 说,产品数组:

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

    怎么做?

3 个答案:

答案 0 :(得分:4)

首先,您需要知道Symfony2验证器还没有准备好这么做。我花了一些时间和一些Symfony2源码阅读来为你的案例找到一个可行的解决方案,我的解决方案并不那么自然。

我创建了一个带有验证器,数组和yaml配置文件的类,这样你就可以按照自己的意愿行事。这个类扩展了来自Symfony的YamlFileLoader以访问受保护的parseNodes方法:这不是很漂亮,但这是我发现将自定义Yaml配置文件转换为Constraint对象数组的唯一方法

所以我们在这里。我给你我的代码,你需要根据你自己的上下文替换一些命名空间。

首先,为我们的演示创建一个控制器:

    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');
    }

然后创建一个名为ArrayValidator.php的类。再次,注意命名空间。

<?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;
    }

}

最后,在$ product数组中使用不同的值对其进行测试。

默认情况下:

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

将显示:

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

如果我们将值更改为:

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

你会得到:

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."
  }
}

希望这有帮助。

答案 1 :(得分:1)

验证数组的方法很简单,我在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);

或者您可以直接创建带约束的表单

$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();

}

这段代码可以解决你的第二点,但是对于第一点,我建议你编写一个自定义类,将你的yaml定义转换为带有实例化验证约束对象的有效约束数组,或者直接给出一个表单!

我不知道有哪个班级准备在symfony2中这样做。

我在其他没有良好数据模型的项目中完成了这项工作,但在symfony中,您可以创建模型并定义与之相关的验证。

答案 2 :(得分:-1)

验证中的

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

和您的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));
            }
        }
    }
?>