Укажите разные группы проверки для каждого элемента коллекции в Symfony 2?

[ Документация о коллекции ] При встраивании форм (тип коллекции) можно указать группы проверки для каждого элемента на основе текущего элемента? Вроде не работает банкомат.

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 должен быть проверен против Default, Edit группы пока тег 2 против Default только группа.

TagType форма, определяющая динамические группы проверки

Основываясь на базовых данных, если тег новый, он получает Default группа, если существует Default, Create группы:

// ...

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 как предложено здесь:

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)

1 ответ

Полный код, который я использовал для проверки своего ответа, находится по https://github.com/guilro/SymfonyTests/tree/SO21276662.

Valid ограничение заставляет Validator проверять внедренный объект, и AFAIK API не предоставляет способа установить группу проверки.

Но на более высоком уровне мы можем попросить компонент Form каскадировать ValidationListener для всех встраиваемых форм и использовать API компонента Form для установки группы проверки.

Мы должны использовать:

  • 'cascade_validation' => true опция в FormBuilder, на всех уровнях. Это установлено в false по умолчанию.
  • обратный вызов в настройках TagType для установки группы проверки. (Вы были на правильном пути.)
  • 'error_bubbling' => 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,
    ));
  }
}
Другие вопросы по тегам