将自定义参数传递给 Symfony2 中的自定义 ValidationConstraint

Posted

技术标签:

【中文标题】将自定义参数传递给 Symfony2 中的自定义 ValidationConstraint【英文标题】:Pass custom parameters to custom ValidationConstraint in Symfony2 【发布时间】:2013-03-15 07:30:55 【问题描述】:

我正在 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 作为参数传递,但我无法将$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(...);
        
    

This solution 可能正是我正在寻找的那个,但我的表单没有绑定到实体,也不应该是(我从 getData() 方法获取数据)。

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

【问题讨论】:

【参考方案1】:

好吧,我对表单/验证组件不太熟悉,但您可以使用 Hidden field 和作者的姓名/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(),
            ))
        ;
    

    // ...

【讨论】:

感谢您的回答,但出于安全原因,我认为我不能这样做。实际上,用户可以更改表单中的author 字段以匹配任何其他作者,从而绕过限制。 感谢您编辑我的问题,我不知道 Markdown 语法高亮 :)【参考方案2】:

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

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

【讨论】:

感谢您的回答,将作者注入约束就可以了!我试图将作者注入无效的 ValidatorConstraint !【参考方案3】:

在 Cerad 的帮助下,我终于弄明白了。要注入需要从ConstraintValidator::validate() 方法访问的自定义参数,您需要在Constraint 中将它们作为options 传递。

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

【讨论】:

请注意,在 Symfony 2.7 中,自定义参数不是直接在 $options 数组中找到的,而是在一个 'value' 键中。所以对于这个例子,你会从 $options['value']['author'] 获得作者。如果您使用注释声明断言,您将编写如下内容:@CustomAssert\CustomDateTime("dateFormat" : Enum::DATE_FORMAT) 您应该定义 $author 属性 public 以便您可以在构造函数中调用 parent::__construct($options);【参考方案4】:

接受的答案对我使用 Symfony 框架 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))
                )
        ));
    
 

【讨论】:

以上是关于将自定义参数传递给 Symfony2 中的自定义 ValidationConstraint的主要内容,如果未能解决你的问题,请参考以下文章

如何将自定义注释中的参数传递给库中的 WebSecurityConfigurer

将自定义启动参数传递给 detox

如何将自定义参数传递给事件处理程序

将自定义表单参数传递给表单集

Django 将自定义表单参数传递给 ModelFormset

Javascript:将自定义参数传递给回调函数