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

[关于集合的文档] 嵌入表单时(集合类型)是否可以根据当前项目为每个项目指定验证组?ATM 好像不行.

[Documentation about collection] When embedding forms (collection type) is possible to specify validation groups for each item, based on the current item? It seems not working 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)添加了一个新标签:

For example we have two tags (tag 1 and tag 2) and a new tag is added using the "Add" button (via JavaScript):

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

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

Tag 1 should be validated against Default, Edit groups while tag 2 against Default group only.

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

Based on the underlying data, if tag is new it gets Default group, if exists Default, Create groups:

// ...

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 定义两个属性的示例(省略了访问器):

Tag entity with a constraint in the "Edit" group

An example with Tag defining two properties (accessors omitted):

class Tag
{
    /**
     * @AssertNotBlank()
     */
    protected $name;

    /**
     * @AssertNotBlank(groups={"Edit"})
     * @AssertLength(max="255")
     */
    protected $description;

    // ...
}

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

For an existing tag: description should not be blank. For a new tag: description can be blank.

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

Just edit an existing tag and leave the description blank. The form validates but the validator service shows errors:

$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 作为建议 这里:

Update 1: I've tried (without success) with a static method determineValidationGroups as suggested 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(
            'AcmeTaskBundleEntityTag',
            'determineValidationGroups'
        ),
    ));
}

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

Output with just one existing tag and one created using the "add tag" link seems correct. But no errors for the existing tag leaving the description blank:

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)

推荐答案

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

Valid 约束强制 Validator 验证嵌入对象,而 AFAIK API 没有提供设置验证组的方法.

Valid constraint force Validator to validate embed object, and AFAIK the API provides no way to set validation group.

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

But at a higher level, we can ask Form component to cascade ValidationListener to all embed forms, and use the Form component API to set validation group.

我们必须使用:

  • 'cascade_validation' =>在 FormBuilder 中,所有级别的 true 选项.默认设置为 false.
  • 在 TagType 设置中设置验证组的回调.(你走对了.)
  • 'error_bubbling' =>false,因为它在集合中默认为 true
  • 'cascade_validation' => true option in the FormBuilder, at all levels. It is set to false by default.
  • a callback in TagType settings to set validation group. (You were on the right track.)
  • 'error_bubbling' => false, as it is true by default in collections

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

and we're done, we can display the form with all errors next to corresponding fields.

在 TaskType.php 中:

in 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' => 'AcmeTaskBundleEntityTask',
        'cascade_validation' => true
    ));
  }
}

在 TagType.php 中:

in 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' => 'AcmeTaskBundleEntityTag',
        'validation_groups' => function(FormInterface $form) {
            if ($form->getData() !== null && null !== $form->getData()->getId())
            {
                return array('Edit');
            }
            return array('Default');
        },
        'error_bubbling' => false,
    ));
  }
}

相关文章