Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pass custom parameters to custom ValidationConstraint in Symfony2

I'm creating a form in Symfony2. The form only contains one book field which allows the user to choose between a list of Books entities. I need to check whether the selected Book belongs to an Author I have in my controller.

public class MyFormType extends AbstractType
{
    protected $author;

    public function __construct(Author $author) {
        $this->author = $author;
    }

    public function buildForm(FormBuilderInterface $builder, array $options) {
        $builder->add('book', 'entity', array('class' => 'AcmeDemoBundle:Book', 'field' => 'title');
    }

    // ...
}

I want to check, after submitting the form, that the selected Book is written by the $author in my controller:

public class MyController
{
    public function doStuffAction() {
        $author = ...;
        $form = $this->createForm(new MyFormType($author));
        $form->bind($this->getRequest());

        // ...
    }
}

Unfortunately, I cannot find any way to do that. I tried creating a custom validator constraint as explained in The Cookbook, but while I can pass the EntityManager as parameter by defining the validator as a service, I cannot pass the $author from the controller to the validator constraint.

class HasValidAuthorConstraintValidator extends ConstraintValidator
{
    private $entityManager;

    public function __construct(EntityManager $entityManager) {
        $this->entityManager = $entityManager;
    }

    public function validate($value, Constraint $constraint) {
        $book = $this->entityManager->getRepository('book')->findOneById($value);
        $author = ...; // That's the data I'm missing

        if(!$book->belongsTo($author))
        {
            $this->context->addViolation(...);
        }
    }
}

This solution could be exactly the one that I was looking for, but my form is not bound to an Entity and is not meant to be (I'm getting the data from the getData() method).

Is there a solution to my problem ? This must be a common case but I really don't know how to solve it.

like image 541
Phen Avatar asked Mar 25 '13 17:03

Phen


2 Answers

I finally figured it out, with the help from Cerad. To inject custom parameters that need to be accessed from the ConstraintValidator::validate() method, you need to pass them as options in the Constraint.

public class HasValidAuthorConstraint extends Constraint
{
    protected $author;

    public function __construct($options)
    {
        if($options['author'] and $options['author'] instanceof Author)
        {
            $this->author = $options['author'];
        }
        else
        {
            throw new MissingOptionException("...");
        }
    }

    public function getAuthor()
    {
        return $this->author;
    }
}

And, in the ConstraintValidator:

class HasValidAuthorConstraintValidator extends ConstraintValidator
{
    private $entityManager;

    public function __construct(EntityManager $entityManager) {
        $this->entityManager = $entityManager;
    }

    public function validate($value, Constraint $constraint) {
        $book = $this->entityManager->getRepository('book')->findOneById($value);
        $author = $this->constraint->getAuthor();

        if(!$book->isAuthor($author))
        {
            $this->context->addViolation(...);
        }
    }
}

Last but not least, you have to pass the parameter to the Validator:

public function buildForm(FormBuilderInterface $builder, array $options) {
    $builder->add('book', 'entity', array(
        'class' => 'AcmeDemoBundle:Book',
        'field' => 'title',
        'constraints' => array(
            new HasValidAuthorConstraint(array(
                'author' => $this->author
            ))
        )
    ));
}
like image 122
Phen Avatar answered Oct 31 '22 22:10

Phen


Start by adding a setAuthor method to your constraint and then tweaking the validate method. The trick then is to determine the best place to call it.

It's not clear exactly how you are binding the validator to your book. Are you using validation.yml or doing something inside of the form?

like image 3
Cerad Avatar answered Oct 31 '22 23:10

Cerad