Укажите различные группы проверки для каждого элемента коллекции в Symfony 2?
[Документация о коллекции]. При встраивании форм (тип коллекции) можно указать группы проверки для каждого элемента на основе текущего элемента? Кажется, что не работает 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 должен быть проверен против групп Default
, Edit
, тогда как тег 2 только для группы Default
.
TagType
форма, определяющая группы динамической валидации
На основе базовых данных, если тег является новым, он получает группу Default
, если существует 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
, определяющий два свойства (пропущенные аксессоры):
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
Ограничить силу Валидатор для проверки встроенного объекта, а 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,
));
}
}