[Form] Introduce validation events
| Q | A |
|---|---|
| Branch? | 6.2 |
| Bug fix? | no |
| New feature? | yes |
| Deprecations? | no |
| Tickets | Fix #47046 |
| License | MIT |
| Doc PR | https://github.com/symfony/symfony-docs/pull/17229 |
Inspired from https://github.com/symfony/symfony/pull/38479 (@alcaeus, I coud not reuse your commit because you deleted your branch)
With some changes:
- I removed the BC break
form.pre_validateandform.post_validateevents are triggered individually to all Form items, not only the to the root form (same as all others existing form events)
Regarding the second point, for example, when working with nested forms, if the form.post_validate event is attached to a child form:
$event->getForm()->isValid()will return the validity for the current form item only (the nested one)- If you want to check if the entire form is valid form a nested form, you will have to check the root form:
$event->getForm()->getRoot()->isValid()
Usage example:
Thy are working in the same way as existing form events work.
class MyFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->addEventListener(ValidatorFormEvents::PRE_VALIDATE, function (FormEvent $event) {
// ...
})
->addEventListener(ValidatorFormEvents::POST_VALIDATE, function (FormEvent $event) {
$event->getForm()->isValid(); // Check if current form item is valid
$event->getForm()->getRoot()->isValid(); // Check if entire form is valid
})
;
}
}
Why are those events required?
Can't we just add a POST_SUBMIT event with a lower priority than the validation event?
Yes, this solution can work, but ONLY on the root form.
Because the validation of the form is triggered only by the root form, and events are triggered individually on all form items, starting from ancestors and ending by the root form.
This means, we currently have no way to know if a nested form is valid when attaching a POST_SUBMIT event on it.
In case you want to review, I saw you participated to the previous PR @d-ph @silasjoisten
Hi.
Just wanted to say that I'm happy to see this feature gaining traction, and looking forward to seeing it in the stock Symfony code. Unfortunately, it's unlikely I'd review the PR in depth, however I did skim through the change, and it looks good to me.
May I ask a quick question though, just to double-check: Given the following case:
- A root form
foolistening on the postValidate event. - A child form
fooChildalso listenting on the postValidate event. - The form
foois being submitted.
Then:
fooChild's postValidate is going to be called first, and thenfoo's postValidate is going to be called second. I.e. The new events are dispatched recursively, deepest child form first?- If
fooChildadds a new FormError on itself in the postValidate listener, thefoo's postValidate listener will be able to see that there are validation errors by calling either:$event->getForm()->isValid(), or$event->getForm()->get('fooChild')->isValid()?
Thanks.
Btw Seb33300 search for "->idValid(" on this page and consider correcting the typos, because it takes a few seconds to conclude that it's a typo. Cheers.
@d-ph typo fixed. Thanks.
POST_VALIDATE event happens AFTER the form validation has been processed ON ALL form items.
-
Yes, child form event will be dispatched first. The root form event will be the last one to be dispatched. (same order as others form events)
-
Yes, you can add a new error to the form using
$event->getForm()->addError()inside this event. If you do so, the form will be considered invalid after that. When the event will be propagated to parent form, the parent form will also be considered as invalid. Because a form is valid only if all his children are valid.
I faced the same use case. I did not test this PR, but I think this can be a good feature 👍🏼