Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Building Symfony 2 Custom Validator that Uses Multiple Fields

Tags:

symfony

I'm building a custom validator that needs to validate the value from TWO form fields in the db in order to get this constraint to pass.

My question is this: the ContractValidator's validate method only has one $value in it's signature so how do I get access to the values from more than just a single field to do the validation?

Here is a typical custom validator:

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

Here's an example of a form class with some validations set:

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
    );
  }  
}
like image 704
doremi Avatar asked Nov 13 '12 15:11

doremi


1 Answers

Any custom validator that extends ConstraintValidator has access to the $context property. $context is an instance of ExecutionContext that gives you access to submitted data:

Example:

<?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();
        /* ... */
    }
}
like image 101
b.b3rn4rd Avatar answered Sep 20 '22 12:09

b.b3rn4rd