19

我正在构建一个自定义验证器,它需要验证数据库中两个表单字段的值,以便让这个约束通过。

我的问题是:ContractValidator 的 validate 方法在其签名中只有一个 $value,那么我如何从多个字段中访问这些值来进行验证?

这是一个典型的自定义验证器:

namespace Acme\WebsiteBundle\Validator\Constraints;

use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;

class MyCustomValidator extends ConstraintValidator
{
  public function validate($value, Constraint $constraint)
  {
    // check $value and return an error
    // but in my case, i want the value from more than one form field to do a validation
    // why? i'm checking that two pieces of information (ssn + dob year) match
    // the account the user is registering for
  }
}

这是一个带有一些验证集的表单类的示例:

namespace ACME\WebsiteBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\Validator\Constraints\Collection;
use Symfony\Component\Validator\Constraints\MinLength;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Constraints\Regex;
use ACME\WebsiteBundle\Validator\Constraints\UsernameAvailable;

class AccountRegistration extends AbstractType
{
  public function buildForm(FormBuilderInterface $builder, array $options)
  {
    $builder->add('ssn', 'number', array(
      'max_length' => 9, 
      'required' => true,
      'error_bubbling' => true)
    );

    $builder->add('year_of_birth', 'choice', array(
      'choices'  => range(date("Y") - 100, date("Y")),
      'required' => true,
      'empty_value' => 'Select ...',
      'label'    => 'Year of Birth',
      'error_bubbling' => true)
    );

    $builder->add('username', 'text', array(
      'required' => true,
      'error_bubbling' => true)
    );

    $builder->add('password', 'password', array(
      'max_length' => 25,
      'required' => true,
      'error_bubbling' => true)
    );

    $builder->add('security_question', 'choice', array(
      'empty_value' => 'Select ...',
      'choices' => array(),
      'label' => 'Security Question',
      'required' => true,
      'error_bubbling' => true)
    );

    $builder->add('security_question_answer', 'text', array(
      'label' => 'Answer',
      'required' => true,
      'error_bubbling' => true)
    );
  }

  public function getName()
  {
    return 'account_registration';
  }

  public function getDefaultOptions(array $options)
  {

    $collectionConstraint = new Collection(array(
      'allowExtraFields' => true, 
      'fields' => array(
        'ssn'  => array(new MinLength(array('limit' => 9, 'message' => 'too short.')), new NotBlank()),
        'year_of_birth' => array(new NotBlank()),
        'username' => array(new NotBlank(), new UsernameAvailable()),
        'password' => array(new NotBlank(), new Regex(array(
          'message' => 'password must be min 8 chars, contain at least 1 digit',
          'pattern' => "((?=.*\d)(?=.*[a-z]).{8,25})"))
        ),
        'security_question' => array(new NotBlank()),
        'security_question_answer' => array(new NotBlank()))
      )
    );

    return array(
        'csrf_protection' => true,
        'csrf_field_name' => '_token',
        'intention'       => 'account_registration',
        'validation_constraint' => $collectionConstraint
    );
  }  
}
4

2 回答 2

30

任何扩展的自定义验证器ConstraintValidator都可以访问该$context属性。$context是一个实例,ExecutionContext可让您访问提交的数据:

例子:

<?php

namespace My\Bundle\MyBundle\Validator\Constraints;

use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;


class AppointorRoleValidator extends ConstraintValidator
{

    public function validate($value, Constraint $constraint)
    {
        $values = $this->context->getRoot()->getData();
        /* ... */
    }
}
于 2014-08-21T05:54:20.097 回答
4

您需要CLASS_CONSTRAINT按照食谱中的说明使用。然后,您通过了整个班级,并且可以使用该班级的任何属性。在上面的示例中,这意味着不是$value蜂鸣一个字符串/整数,而是您要验证的整个对象。

您唯一需要更改的是getTargets()函数,它必须返回self::CLASS_CONSTRAINT

还要确保在类级别而不是属性级别定义验证器。如果您使用注释,这意味着验证器必须在类定义之上进行描述,而不是在一个特定属性定义之上:

/**
  * @MyValidator\SomeValidator
  */
class MyClass {

}
于 2012-11-13T16:15:21.463 回答