22

我正在 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());

        // ...
    }
}

不幸的是,我找不到任何方法来做到这一点。我尝试按照The 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's the data I'm missing

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

该解决方案可能正是我正在寻找的解决方案,但我的表单未绑定到实体,也不应该是(我从getData()方法中获取数据)。

我的问题有解决方案吗?这一定是一个常见的情况,但我真的不知道如何解决它。

4

4 回答 4

32

在 Cerad 的帮助下,我终于弄明白了。要注入需要从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(...);
        }
    }
}

最后但同样重要的是,您必须将参数传递给验证器:

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
            ))
        )
    ));
}
于 2013-03-26T16:12:00.027 回答
3

首先将 setAuthor 方法添加到您的约束,然后调整 validate 方法。诀窍是确定调用它的最佳位置。

目前尚不清楚您如何将验证器绑定到您的书。您是在使用validation.yml 还是在表单内做某事?

于 2013-03-26T14:56:13.510 回答
1

好吧,我对表单/验证组件不是那么熟悉,但是您可以使用带有作者姓名/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(),
            ))
        ;
    }

    // ...
}
于 2013-03-25T19:05:00.357 回答
-2

接受的答案对我使用 Symfony Framework version 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))
                )
        ));
    }
} 
于 2016-04-11T17:53:15.090 回答