[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.
The TaskType
form adding a collection of tags:
// src/Acme/TaskBundle/Form/Type/TaskType.php
// ...
public function buildForm(FormBuilderInterface $builder, array $options)
{
// ...
$builder->add('tags', 'collection', array(
// ...
'by_reference' => false,
));
}
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)
Tag 1 should be validated against Default
, Edit
groups while tag 2 against Default
group only.
TagType
form defining dynamic validation groups
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
entity with a constraint in the "Edit" group
An example with Tag
defining two properties (accessors omitted):
class Tag
{
/**
* @Assert\NotBlank()
*/
protected $name;
/**
* @Assert\NotBlank(groups={"Edit"})
* @Assert\Length(max="255")
*/
protected $description;
// ...
}
For an existing tag: description should not be blank. For a new tag: description can be blank.
Proof form is valid, validator shows errors (wrong!)
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.')
// ...
}
Output:
Existing tag #863, errors: 1
Form is valid.
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;
}
In TagType
form:
/**
* {@inheritdoc}
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
// ...
'validation_groups' => array(
'Acme\TaskBundle\Entity\Tag',
'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)
The complete code I used to test my answer is on https://github.com/guilro/SymfonyTests/tree/SO21276662.
Valid
constraint force Validator to validate embed object, and AFAIK the API provides no way to set validation group.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.
We must use :
'cascade_validation' => true
option in the FormBuilder, at all levels. It is set tofalse
by default.'error_bubbling' => false
, as it is true by default in collectionsand we're done, we can display the form with all errors next to corresponding fields.
in TaskType.php :
in TagType.php :