From 0d09f16df5676306a35bc29d5bbac32a8b490815 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 30 Jun 2025 13:24:03 +0200 Subject: [PATCH] [Form][Validator] Merge all articles about using validation groups in forms --- _build/redirection_map | 3 + form/button_based_validation.rst | 36 ---- form/data_based_validation.rst | 72 -------- form/validation_group_service_resolver.rst | 58 ------- form/validation_groups.rst | 164 +++++++++++++++--- forms.rst | 2 - .../types/options/validation_groups.rst.inc | 57 +----- reference/forms/types/submit.rst | 24 +-- 8 files changed, 155 insertions(+), 261 deletions(-) delete mode 100644 form/button_based_validation.rst delete mode 100644 form/data_based_validation.rst delete mode 100644 form/validation_group_service_resolver.rst diff --git a/_build/redirection_map b/_build/redirection_map index 896c58a3022..ec088ad2209 100644 --- a/_build/redirection_map +++ b/_build/redirection_map @@ -570,3 +570,6 @@ /components/serializer /serializer /serializer/custom_encoder /serializer/encoders#serializer-custom-encoder /components/string /string +/form/button_based_validation /form/validation_groups +/form/data_based_validation /form/validation_groups +/form/validation_group_service_resolver /form/validation_groups diff --git a/form/button_based_validation.rst b/form/button_based_validation.rst deleted file mode 100644 index 47f2673b079..00000000000 --- a/form/button_based_validation.rst +++ /dev/null @@ -1,36 +0,0 @@ -How to Choose Validation Groups Based on the Clicked Button -=========================================================== - -When your form contains multiple submit buttons, you can change the validation -group depending on which button is used to submit the form. For example, -consider a form in a wizard that lets you advance to the next step or go back -to the previous step. Also assume that when returning to the previous step, -the data of the form should be saved, but not validated. - -First, we need to add the two buttons to the form:: - - $form = $this->createFormBuilder($task) - // ... - ->add('nextStep', SubmitType::class) - ->add('previousStep', SubmitType::class) - ->getForm(); - -Then, we configure the button for returning to the previous step to run -specific validation groups. In this example, we want it to suppress validation, -so we set its ``validation_groups`` option to false:: - - $form = $this->createFormBuilder($task) - // ... - ->add('previousStep', SubmitType::class, [ - 'validation_groups' => false, - ]) - ->getForm(); - -Now the form will skip your validation constraints. It will still validate -basic integrity constraints, such as checking whether an uploaded file was too -large or whether you tried to submit text in a number field. - -.. seealso:: - - To see how to use a service to resolve ``validation_groups`` dynamically - read the :doc:`/form/validation_group_service_resolver` article. diff --git a/form/data_based_validation.rst b/form/data_based_validation.rst deleted file mode 100644 index b01bea10b16..00000000000 --- a/form/data_based_validation.rst +++ /dev/null @@ -1,72 +0,0 @@ -How to Choose Validation Groups Based on the Submitted Data -=========================================================== - -If you need some advanced logic to determine the validation groups (e.g. -based on submitted data), you can set the ``validation_groups`` option -to an array callback:: - - use App\Entity\Client; - use Symfony\Component\OptionsResolver\OptionsResolver; - - // ... - public function configureOptions(OptionsResolver $resolver): void - { - $resolver->setDefaults([ - 'validation_groups' => [ - Client::class, - 'determineValidationGroups', - ], - ]); - } - -This will call the static method ``determineValidationGroups()`` on the -``Client`` class after the form is submitted, but before validation is -invoked. The Form object is passed as an argument to that method (see next -example). You can also define whole logic inline by using a ``Closure``:: - - use App\Entity\Client; - use Symfony\Component\Form\FormInterface; - use Symfony\Component\OptionsResolver\OptionsResolver; - - // ... - public function configureOptions(OptionsResolver $resolver): void - { - $resolver->setDefaults([ - 'validation_groups' => function (FormInterface $form): array { - $data = $form->getData(); - - if (Client::TYPE_PERSON == $data->getType()) { - return ['person']; - } - - return ['company']; - }, - ]); - } - -Using the ``validation_groups`` option overrides the default validation -group which is being used. If you want to validate the default constraints -of the entity as well you have to adjust the option as follows:: - - use App\Entity\Client; - use Symfony\Component\Form\FormInterface; - use Symfony\Component\OptionsResolver\OptionsResolver; - - // ... - public function configureOptions(OptionsResolver $resolver): void - { - $resolver->setDefaults([ - 'validation_groups' => function (FormInterface $form): array { - $data = $form->getData(); - - if (Client::TYPE_PERSON == $data->getType()) { - return ['Default', 'person']; - } - - return ['Default', 'company']; - }, - ]); - } - -You can find more information about how the validation groups and the default constraints -work in the article about :doc:`validation groups `. diff --git a/form/validation_group_service_resolver.rst b/form/validation_group_service_resolver.rst deleted file mode 100644 index 82a6f65d6ec..00000000000 --- a/form/validation_group_service_resolver.rst +++ /dev/null @@ -1,58 +0,0 @@ -How to Dynamically Configure Form Validation Groups -=================================================== - -Sometimes you need advanced logic to determine the validation groups. If they -can't be determined by a callback, you can use a service. Create a service -that implements ``__invoke()`` which accepts a ``FormInterface`` as a -parameter:: - - // src/Validation/ValidationGroupResolver.php - namespace App\Validation; - - use Symfony\Component\Form\FormInterface; - - class ValidationGroupResolver - { - public function __construct( - private object $service1, - private object $service2, - ) { - } - - public function __invoke(FormInterface $form): array - { - $groups = []; - - // ... determine which groups to apply and return an array - - return $groups; - } - } - -Then in your form, inject the resolver and set it as the ``validation_groups``:: - - // src/Form/MyClassType.php; - namespace App\Form; - - use App\Validation\ValidationGroupResolver; - use Symfony\Component\Form\AbstractType; - use Symfony\Component\OptionsResolver\OptionsResolver; - - class MyClassType extends AbstractType - { - public function __construct( - private ValidationGroupResolver $groupResolver, - ) { - } - - // ... - public function configureOptions(OptionsResolver $resolver): void - { - $resolver->setDefaults([ - 'validation_groups' => $this->groupResolver, - ]); - } - } - -This will result in the form validator invoking your group resolver to set the -validation groups returned when validating. diff --git a/form/validation_groups.rst b/form/validation_groups.rst index 4addc1ba1a7..3157ef7bc5b 100644 --- a/form/validation_groups.rst +++ b/form/validation_groups.rst @@ -1,39 +1,163 @@ -How to Define the Validation Groups to Use -========================================== +Configuring Validation Groups in Forms +====================================== -Validation Groups ------------------ +If the object handled in your form uses :doc:`validation groups `, +you need to specify which validation group(s) the form should apply. -If your object takes advantage of :doc:`validation groups `, -you'll need to specify which validation group(s) your form should use. Pass -this as an option when :ref:`creating forms in controllers `:: +To define them when :ref:`creating forms in classes `, +use the ``configureOptions()`` method:: + + use Symfony\Component\OptionsResolver\OptionsResolver; + + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + // ... + 'validation_groups' => ['registration'], + ]); + } + +When :ref:`creating forms in controllers `, pass +it as a form option:: $form = $this->createFormBuilder($user, [ 'validation_groups' => ['registration'], ])->add(/* ... */); -When :ref:`creating forms in classes `, add the -following to the ``configureOptions()`` method:: +In both cases, *only* the ``registration`` group will be used to validate the +object. To apply the ``registration`` group *and* all constraints not in any +other group, add the special ``Default`` group:: + + [ + // ... + 'validation_groups' => ['Default', 'registration'], + ] +.. note:: + + You can use any name for your validation groups. Symfony recommends using + "lower snake case" (e.g. ``foo_bar``), while automatically generated + groups use "UpperCamelCase" (e.g. ``Default``, ``SomeClassName``). + +Choosing Validation Groups Based on the Clicked Button +------------------------------------------------------ + +When your form has :doc:`multiple submit buttons `, you +can change the validation group based on the clicked button. For example, in a +multi-step form like the following, you might want to skip validation when +returning to a previous step:: + + $form = $this->createFormBuilder($task) + // ... + ->add('nextStep', SubmitType::class) + ->add('previousStep', SubmitType::class) + ->getForm(); + +To do so, configure the validation groups of the ``previousStep`` button to +``false``, which is a special value that skips validation:: + + $form = $this->createFormBuilder($task) + // ... + ->add('previousStep', SubmitType::class, [ + 'validation_groups' => false, + ]) + ->getForm(); + +Now the form will skip your validation constraints when that button is clicked. +It will still validate basic integrity constraints, such as checking whether an +uploaded file was too large or whether you tried to submit text in a number field. + +Choosing Validation Groups Based on Submitted Data +-------------------------------------------------- + +To determine validation groups dynamically based on submitted data, use a +callback. This is called after the form is submitted, but before validation is +invoked. The callback receives the form object as its first argument:: + + use App\Entity\Client; + use Symfony\Component\Form\FormInterface; use Symfony\Component\OptionsResolver\OptionsResolver; public function configureOptions(OptionsResolver $resolver): void { $resolver->setDefaults([ - // ... - 'validation_groups' => ['registration'], + 'validation_groups' => function (FormInterface $form): array { + $data = $form->getData(); + + if (Client::TYPE_PERSON === $data->getType()) { + return ['Default', 'person']; + } + + return ['Default', 'company']; + }, ]); } -In both of these cases, *only* the ``registration`` validation group will -be used to validate the underlying object. To apply the ``registration`` -group *and* all constraints that are not in a group, use:: +.. note:: + + Adding ``Default`` to the list of validation groups is common but not mandatory. + See the main :doc:`article about validation groups ` to + learn more about validation groups and the default constraints. - 'validation_groups' => ['Default', 'registration'] +You can also pass a static class method callback:: -.. note:: + 'validation_groups' => [Client::class, 'determineValidationGroups'] + +Choosing Validation Groups via a Service +---------------------------------------- + +If validation group logic requires services or can't fit in a closure, use a +dedicated validation group resolver service. The class of this service must +be invokable and receives the form object as its first argument:: + + // src/Validation/ValidationGroupResolver.php + namespace App\Validation; + + use Symfony\Component\Form\FormInterface; + + class ValidationGroupResolver + { + public function __construct( + private object $service1, + private object $service2, + ) { + } + + public function __invoke(FormInterface $form): array + { + $groups = []; + + // ... determine which groups to return + + return $groups; + } + } + +Then use the service in your form type:: + + namespace App\Form; + + use App\Validation\ValidationGroupResolver; + use Symfony\Component\Form\AbstractType; + use Symfony\Component\OptionsResolver\OptionsResolver; + + class MyClassType extends AbstractType + { + public function __construct( + private ValidationGroupResolver $groupResolver, + ) { + } + + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + 'validation_groups' => $this->groupResolver, + ]); + } + } + +Learn More +---------- - You can choose any name for your validation groups, but Symfony recommends - using "lower snake case" names (e.g. ``foo_bar``) in contrast with the - automatic validation groups created by Symfony, which use "upper camel case" - (e.g. ``Default``, ``SomeClassName``). +For more information about how validation groups work, see +:doc:`/validation/groups`. diff --git a/forms.rst b/forms.rst index 38006169cdb..bea93edb25d 100644 --- a/forms.rst +++ b/forms.rst @@ -1003,8 +1003,6 @@ Validation: :maxdepth: 1 /form/validation_groups - /form/validation_group_service_resolver - /form/button_based_validation /form/disabling_validation Misc.: diff --git a/reference/forms/types/options/validation_groups.rst.inc b/reference/forms/types/options/validation_groups.rst.inc index 1f5c9a597a3..6957bf203a3 100644 --- a/reference/forms/types/options/validation_groups.rst.inc +++ b/reference/forms/types/options/validation_groups.rst.inc @@ -1,59 +1,14 @@ ``validation_groups`` ~~~~~~~~~~~~~~~~~~~~~ -**type**: ``array``, ``string``, ``callable``, :class:`Symfony\\Component\\Validator\\Constraints\\GroupSequence` or ``null`` **default**: ``null`` +**type**: ``array``, ``string``, ``callable``, :class:`Symfony\\Component\\Validator\\Constraints\\GroupSequence`, or ``null`` **default**: ``null`` -This option is only valid on the root form and is used to specify which -groups will be used by the validator. +This option is only valid on the root form. It specifies which validation groups +will be used by the validator. -For ``null`` the validator will just use the ``Default`` group. - -If you specify the groups as an array or string they will be used by the -validator as they are:: - - public function configureOptions(OptionsResolver $resolver): void - { - $resolver->setDefaults([ - 'validation_groups' => 'Registration', - ]); - } - -This is equivalent to passing the group as array:: - - 'validation_groups' => ['Registration'], - -The form's data will be :doc:`validated against all given groups `. - -If the validation groups depend on the form's data a callable may be passed to -the option. Symfony will then pass the form when calling it:: - - use Symfony\Component\Form\FormInterface; - use Symfony\Component\OptionsResolver\OptionsResolver; - - // ... - public function configureOptions(OptionsResolver $resolver): void - { - $resolver->setDefaults([ - 'validation_groups' => function (FormInterface $form): array { - $entity = $form->getData(); - - return $entity->isUser() ? ['User'] : ['Company']; - }, - ]); - } - -.. seealso:: - - You can read more about this in :doc:`/form/data_based_validation`. - -.. note:: - - When your form contains multiple submit buttons, you can change the - validation group depending on :doc:`which button is used ` - to submit the form. - - If you need advanced logic to determine the validation groups have - a look at :doc:`/form/validation_group_service_resolver`. +If set to ``null``, the validator will use only the ``Default`` group. For the +other possible values, see the main article about +:doc:`using validation groups in Symfony forms ` In some cases, you want to validate your groups step by step. To do this, you can pass a :class:`Symfony\\Component\\Validator\\Constraints\\GroupSequence` diff --git a/reference/forms/types/submit.rst b/reference/forms/types/submit.rst index 70fa429685a..5d863fbe8b4 100644 --- a/reference/forms/types/submit.rst +++ b/reference/forms/types/submit.rst @@ -102,28 +102,8 @@ validation_groups **type**: ``array`` **default**: ``null`` When your form contains multiple submit buttons, you can change the validation -group based on the button which was used to submit the form. Imagine a registration -form wizard with buttons to go to the previous or the next step:: - - use Symfony\Component\Form\Extension\Core\Type\SubmitType; - // ... - - $form = $this->createFormBuilder($user) - ->add('previousStep', SubmitType::class, [ - 'validation_groups' => false, - ]) - ->add('nextStep', SubmitType::class, [ - 'validation_groups' => ['Registration'], - ]) - ->getForm(); - -The special ``false`` ensures that no validation is performed when the previous -step button is clicked. When the second button is clicked, all constraints -from the "Registration" are validated. - -.. seealso:: - - You can read more about this in :doc:`/form/data_based_validation`. +group based on the clicked button. Read the article about +:doc:`using validation groups in Symfony forms `. Form Variables -------------- pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy