为 Symfony 2 中集合的每个项目指定不同的验证组?

Posted

技术标签:

【中文标题】为 Symfony 2 中集合的每个项目指定不同的验证组?【英文标题】:Specify different validation groups for each item of a collection in Symfony 2? 【发布时间】:2014-02-12 03:23:04 【问题描述】:

[Documentation about collection] 当嵌入表单(集合类型)可以根据当前项目为每个项目指定验证组时? ATM 好像不行。

TaskType 表单添加标签集合:

// src/Acme/TaskBundle/Form/Type/TaskType.php

// ...
public function buildForm(FormBuilderInterface $builder, array $options)

    // ...

    $builder->add('tags', 'collection', array(
        // ...
        'by_reference' => false,
    ));

例如,我们有两个标签(标签 1 和标签 2),并使用“添加”按钮(通过 javascript)添加了一个新标签:

-----------
| add tag |
-----------
- tag 1 (existing)
- tag 2 (added clicking the "add tag" button)

标签 1 应针对 DefaultEdit 组进行验证,而标签 2 仅针对 Default 组进行验证。

TagType 定义动态验证组的表单

根据基础数据,如果标签是新的,它得到Default组,如果存在DefaultCreate组:

// ...

public function setDefaultOptions(OptionsResolverInterface $resolver)

    $resolver->setDefaults(array(
        'validation_groups' => function (FormInterface $form) 
            $tag = $form->getData();

            $groups = array('Default');
            if (null !== $tag && null !== $tag->getId()) 
                $groups[] = 'Edit';
            

            return $groups;
        
    ));


// ...

Tag“编辑”组中具有约束的实体

Tag 定义两个属性的示例(省略了访问器):

class Tag

    /**
     * @Assert\NotBlank()
     */
    protected $name;

    /**
     * @Assert\NotBlank(groups="Edit")
     * @Assert\Length(max="255")
     */
    protected $description;

    // ...

对于现有标签:描述不应为空。对于新标签:描述可以为空。

证明形式有效,验证器显示错误(错误!)

只需编辑现有标签并将说明留空即可。 表单验证,但验证器服务显示错误

$form = $this->createForm('task', $task)
    ->handleRequest($request);

$validator = $this->get('validator');

if ($form->isValid()) 
    foreach ($task->getTags() as $tag) 
        // Validate against Default, Edit groups
        $errors = $validator->validate($tag, array('Default', 'Edit'));

        if (null !== $tag && null !== $tag->getId()) 
            echo 'Existing tag #'.$tag->getId();
         else 
            echo 'New tag';
        

        echo ', errors: '.count($errors).'<br>';
    

    die('Form is valid.')

    // ...

输出:

Existing tag #863, errors: 1
Form is valid.

更新 1:我已经尝试(但没有成功)使用静态方法 determineValidationGroups 建议 here:

public static function determineValidationGroups(FormInterface $form)

    $groups =  array('Default');
    if ($form->getData() !== null && null !== $form->getData()->getId())
    
        $groups =  array('Edit');
    

    var_dump($groups);

    return $groups;

TagType 形式:

/**
 * @inheritdoc
 */
public function setDefaultOptions(OptionsResolverInterface $resolver)

    $resolver->setDefaults(array(
        // ... 
        'validation_groups' => array(
            'Acme\TaskBundle\Entity\Tag',
            'determineValidationGroups'
        ),
    ));

只有一个现有标签和一个使用“添加标签”链接创建的输出似乎是正确的。但是现有标签没有错误,将描述留空:

array (size=1)
  0 => string 'Edit' (length=4)

array (size=1)
  0 => string 'Edit' (length=4)

rray (size=1)
  0 => string 'Default' (length=7)

rray (size=1)
  0 => string 'Default' (length=7)

【问题讨论】:

请显示您为实体设置验证组的代码 @forgottenbas 您好,感谢您的帮助。我已经更新了我的问题。 编辑了我的答案以使其更清晰。 【参考方案1】:

我用来测试答案的完整代码位于 https://github.com/guilro/SymfonyTests/tree/SO21276662。

Valid 约束强制验证器验证嵌入对象,并且 AFAIK API 没有提供设置验证组的方法。

但是在更高的层次上,我们可以要求 Form 组件将 ValidationListener 级联到所有嵌入的表单,并使用 Form 组件 API 来设置验证组。

我们必须使用:

FormBuilder 中的'cascade_validation' =&gt; true 选项,在所有级别。默认设置为false。 在 TagType 设置中设置验证组的回调。 (你走在正确的轨道上。) 'error_bubbling' =&gt; false,因为默认情况下在集合中是这样的

我们完成了,我们可以在相应字段旁边显示带有所有错误的表单。

在 TaskType.php 中:

class TaskType extends AbstractType

  public function buildForm(FormBuilderInterface $builder, array $options)
  
    $builder
        ->add('name')
        ->add('tags', 'collection', array(
            'type' => 'tag',
            'error_bubbling' => false,
            'allow_add' => true,
            'by_reference' => false,
            'cascade_validation' => true
        ))
    ;
  

  public function setDefaultOptions(OptionsResolverInterface $resolver)
  
    $resolver->setDefaults(array(
        'data_class' => 'Acme\TaskBundle\Entity\Task',
        'cascade_validation' => true
    ));
  

在 TagType.php 中:

class TagType extends AbstractType

  public function buildForm(FormBuilderInterface $builder, array $options)
  
    $builder
        ->add('name')
        ->add('description', 'text', array('required' => false));
  

  public function setDefaultOptions(OptionsResolverInterface $resolver)
  
    $resolver->setDefaults(array(
        'data_class' => 'Acme\TaskBundle\Entity\Tag',
        'validation_groups' => function(FormInterface $form) 
            if ($form->getData() !== null && null !== $form->getData()->getId())
            
                return array('Edit');
            
            return array('Default');
        ,
        'error_bubbling' => false,
    ));
  

【讨论】:

我想知道为什么我们(Symfony 开发人员)都知道并使用Valid 约束来验证嵌入对象,而使用我刚刚发现的'cascade_validation' 选项更有意义。感谢您的提问,通过回答我自己学到了很多东西。 干得好老兄,你终于做到了! 首先感谢您的所有研究工作!现在我正在打电话,所以明天我会读你的答案,我们会看到。与此同时,我发现了另一种解决方法:组序列提供者! 是的,我刚刚在symfony.com/doc/current/book/validation.html#group-sequence 上读过它,听起来也很酷!它提供与 FormBuilder 的validation_group 选项完全相同的功能:动态验证组。我想你的解决方法有点复杂,但作为一个好处,你可以在每个范围内使用验证器(包括Valid),而不仅仅是在表单验证中。干得好 ;) 也许我可以把我的回答社区 wiki 转为你可以和你做的方式? 还对 symfony-doc 进行了 PR,以将其添加到 Collection 类型页面的选项列表中。我们都浪费了很多时间,因为cascade_validation 仅在基本表单类型页面上,并且没有列为 Collection 的继承选项。

以上是关于为 Symfony 2 中集合的每个项目指定不同的验证组?的主要内容,如果未能解决你的问题,请参考以下文章

通过 mongoose 为 mongodb 中集合的文档中的每个嵌套对象创建 ID

python 中集合的属性

java中集合的扩容

C#中集合的使用--ArrayList

Java——关于Java中集合的面试题

Java——12个关于Java中集合的面试题