Передача пользовательских параметров в пользовательский параметр ValidationConstraint в Symfony2

Я создаю форму в Symfony2. Форма содержит только одно поле book, которое позволяет пользователю выбирать между объектами Books. Мне нужно проверить, принадлежит ли выбранный book к Author, который у меня есть в моем контроллере.

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

    // ...
}

Я хочу проверить после отправки формы, что выбранный book записывается $author в моем контроллере:

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

        // ...
    }
}

К сожалению, я не могу найти способ сделать это. Я попытался создать специальное ограничение валидатора, как описано в Cookbook, но пока я могу передать параметр EntityManager as, указав валидатор как услугу, Я не могу передать $author с контроллера на ограничение валидатора.

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 the data I'm missing

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

Это решение может быть именно тем, что я искал, но моя форма не привязана к сущности и не должна быть (я получаю данные из метода getData()).

Есть ли решение моей проблемы? Это должно быть обычным делом, но я действительно не знаю, как его решить.

Ответы

Ответ 1

Я, наконец, понял это с помощью Серада. Чтобы добавить пользовательские параметры, которые необходимо получить из метода ConstraintValidator::validate(), вам необходимо передать их как параметры в 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;
    }
}

И в 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(...);
        }
    }
}

И последнее, но не менее важное: вы должны передать параметр в 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
            ))
        )
    ));
}

Ответ 2

Начните с добавления метода setAuthor к вашему ограничению и затем настройки метода проверки. Трюк тогда должен определить лучшее место, чтобы назвать это.

Не ясно, как именно вы привязываете валидатор к своей книге. Вы используете validation.yml или что-то делаете внутри формы?

Ответ 3

Ну, я не тот familier с компонентом Form/Validation, но вы можете использовать Скрытое поле с именем /id автор и проверьте, не совпадают ли они:

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');
            ->add('author_name', 'hidden', array(
                'data' => $this->author->getId(),
            ))
        ;
    }

    // ...
}

Ответ 4

Принятый ответ не работал у меня, используя Symfony Framework версия 2.1. Вот как я это решил.

class CustomConstraint extends Constraint
{
    public $dependency;
    public $message = 'The error message.';
}

class CustomConstraintValidator extends ConstraintValidator
{
    public function validate($value, Constraint $constraint)
    {
        if (!$constraint->dependency->allows($value)) {
            $this->context->addViolation($constraint->message);
        }
    }
}

class CustomFormType extends AbstractType
{
    private $dependency;

    public function __construct(Dependency $dependency)
    {
        $this->dependency = $dependency;
    }

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('field', 'type', array(
                'constraints' => array(
                    new CustomConstraint(array('dependency' => $this->dependency))
                )
        ));
    }
}