Skip to content

Commit f08b25a

Browse files
committed
[Form] Merge all articles about using validation groups in forms
1 parent 16f868c commit f08b25a

File tree

8 files changed

+152
-261
lines changed

8 files changed

+152
-261
lines changed

_build/redirection_map

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -570,3 +570,6 @@
570570
/components/serializer /serializer
571571
/serializer/custom_encoder /serializer/encoders#serializer-custom-encoder
572572
/components/string /string
573+
/form/button_based_validation /form/validation_groups
574+
/form/data_based_validation /form/validation_groups
575+
/form/validation_group_service_resolver /form/validation_groups

form/button_based_validation.rst

Lines changed: 0 additions & 36 deletions
This file was deleted.

form/data_based_validation.rst

Lines changed: 0 additions & 72 deletions
This file was deleted.

form/validation_group_service_resolver.rst

Lines changed: 0 additions & 58 deletions
This file was deleted.

form/validation_groups.rst

Lines changed: 141 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,160 @@
1-
How to Define the Validation Groups to Use
2-
==========================================
1+
Configuring Validation Groups in Forms
2+
======================================
33

4-
Validation Groups
5-
-----------------
4+
If the object handled in your form uses :doc:`validation groups </validation/groups>`,
5+
you need to specify which validation group(s) the form should apply.
66

7-
If your object takes advantage of :doc:`validation groups </validation/groups>`,
8-
you'll need to specify which validation group(s) your form should use. Pass
9-
this as an option when :ref:`creating forms in controllers <creating-forms-in-controllers>`::
7+
To define them when :ref:`creating forms in classes <creating-forms-in-classes>`,
8+
use the ``configureOptions()`` method::
9+
10+
use Symfony\Component\OptionsResolver\OptionsResolver;
11+
12+
public function configureOptions(OptionsResolver $resolver): void
13+
{
14+
$resolver->setDefaults([
15+
// ...
16+
'validation_groups' => ['registration'],
17+
]);
18+
}
19+
20+
When :ref:`creating forms in controllers <creating-forms-in-controllers>`, pass
21+
it as a form option::
1022

1123
$form = $this->createFormBuilder($user, [
1224
'validation_groups' => ['registration'],
1325
])->add(/* ... */);
1426

15-
When :ref:`creating forms in classes <creating-forms-in-classes>`, add the
16-
following to the ``configureOptions()`` method::
27+
In both cases, *only* the ``registration`` group will be used to validate the
28+
object. To apply the ``registration`` group *and* all constraints not in any
29+
other group, add the special ``Default`` group::
30+
31+
'validation_groups' => ['Default', 'registration']
32+
33+
.. note::
34+
35+
You can use any name for your validation groups. Symfony recommends using
36+
"lower snake case" (e.g. ``foo_bar``), while automatically generated
37+
groups use "UpperCamelCase" (e.g. ``Default``, ``SomeClassName``).
38+
39+
Choosing Validation Groups Based on the Clicked Button
40+
------------------------------------------------------
41+
42+
When your form has :doc:`multiple submit buttons </form/multiple_buttons>`, you
43+
can change the validation group based on the clicked button. For example, in a
44+
multi-step form like the following, you might want to skip validation when
45+
returning to a previous step::
46+
47+
$form = $this->createFormBuilder($task)
48+
// ...
49+
->add('nextStep', SubmitType::class)
50+
->add('previousStep', SubmitType::class)
51+
->getForm();
52+
53+
To do so, configure the validation groups of the ``previousStep`` button to
54+
``false``, which is a special value that skips validation::
1755

56+
$form = $this->createFormBuilder($task)
57+
// ...
58+
->add('previousStep', SubmitType::class, [
59+
'validation_groups' => false,
60+
])
61+
->getForm();
62+
63+
Now the form will skip your validation constraints when that button is clicked.
64+
It will still validate basic integrity constraints, such as checking whether an
65+
uploaded file was too large or whether you tried to submit text in a number field.
66+
67+
Choosing Validation Groups Based on Submitted Data
68+
--------------------------------------------------
69+
70+
To determine validation groups dynamically based on submitted data, use a
71+
callback. This is called after the form is submitted, but before validation is
72+
invoked. The callback receives the form object as its first argument::
73+
74+
use App\Entity\Client;
75+
use Symfony\Component\Form\FormInterface;
1876
use Symfony\Component\OptionsResolver\OptionsResolver;
1977

2078
public function configureOptions(OptionsResolver $resolver): void
2179
{
2280
$resolver->setDefaults([
23-
// ...
24-
'validation_groups' => ['registration'],
81+
'validation_groups' => function (FormInterface $form): array {
82+
$data = $form->getData();
83+
84+
if (Client::TYPE_PERSON === $data->getType()) {
85+
return ['Default', 'person'];
86+
}
87+
88+
return ['Default', 'company'];
89+
},
2590
]);
2691
}
2792

28-
In both of these cases, *only* the ``registration`` validation group will
29-
be used to validate the underlying object. To apply the ``registration``
30-
group *and* all constraints that are not in a group, use::
93+
.. note::
3194

32-
'validation_groups' => ['Default', 'registration']
95+
Adding ``Default`` to the list of validation groups is common but not mandatory.
96+
See the main :doc:`article about validation groups </validation/groups>` to
97+
learn more about validation groups and the default constraints.
3398

34-
.. note::
99+
You can also pass a static class method callback::
100+
101+
'validation_groups' => [Client::class, 'determineValidationGroups']
102+
103+
Choosing Validation Groups via a Service
104+
----------------------------------------
105+
106+
If validation group logic requires services or can't fit in a closure, use a
107+
dedicated validation group resolver service. The class of this service must
108+
be invokable and receives the form object as its first argument::
109+
110+
// src/Validation/ValidationGroupResolver.php
111+
namespace App\Validation;
112+
113+
use Symfony\Component\Form\FormInterface;
114+
115+
class ValidationGroupResolver
116+
{
117+
public function __construct(
118+
private object $service1,
119+
private object $service2,
120+
) {
121+
}
122+
123+
public function __invoke(FormInterface $form): array
124+
{
125+
$groups = [];
126+
127+
// ... determine which groups to return
128+
129+
return $groups;
130+
}
131+
}
132+
133+
Then use the service in your form type::
134+
135+
namespace App\Form;
136+
137+
use App\Validation\ValidationGroupResolver;
138+
use Symfony\Component\Form\AbstractType;
139+
use Symfony\Component\OptionsResolver\OptionsResolver;
140+
141+
class MyClassType extends AbstractType
142+
{
143+
public function __construct(
144+
private ValidationGroupResolver $groupResolver,
145+
) {
146+
}
147+
148+
public function configureOptions(OptionsResolver $resolver): void
149+
{
150+
$resolver->setDefaults([
151+
'validation_groups' => $this->groupResolver,
152+
]);
153+
}
154+
}
155+
156+
Learn More
157+
----------
35158

36-
You can choose any name for your validation groups, but Symfony recommends
37-
using "lower snake case" names (e.g. ``foo_bar``) in contrast with the
38-
automatic validation groups created by Symfony, which use "upper camel case"
39-
(e.g. ``Default``, ``SomeClassName``).
159+
For more information about how validation groups work, see
160+
:doc:`/validation/groups`.

forms.rst

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1003,8 +1003,6 @@ Validation:
10031003
:maxdepth: 1
10041004

10051005
/form/validation_groups
1006-
/form/validation_group_service_resolver
1007-
/form/button_based_validation
10081006
/form/disabling_validation
10091007

10101008
Misc.:

0 commit comments

Comments
 (0)