Pass custom parameters to custom ValidationConstraint in Symfony2

php, symfony, validation

Solution

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

Problem

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.

Original source

Related problems