-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[Form] Introduce validation events for forms #37641
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
src/Symfony/Component/Form/Extension/Validator/Event/PostValidateEvent.php
Show resolved
Hide resolved
src/Symfony/Component/Form/Extension/Validator/EventListener/ValidationListener.php
Outdated
Show resolved
Hide resolved
* In this stage, the form will return a correct value to Form::isValid() and allow for | ||
* further working with the form data. | ||
*/ | ||
final class PostValidateEvent extends FormEvent |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just asking, why not
final class PostValidateEvent extends FormEvent | |
final class PostValidationEvent extends FormEvent |
?
same for PreValidateEvent
and PreValidationEvent
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You've got a point. I'm not entirely convinced by "validate" either, but I'm open for ideas. Any opinions or objections to the suggested wording?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
first i see the listener is called validationListener, thus suggest using validationEvent
but then, i saw the method is called validateForm, so maybe the word validate is ok
(i have in mind the workflow event logic, where event are named after the method) :)
a087bc6
to
6b7549d
Compare
To the folks more familiar with the event dispatcher than me, please let me know if creating event names is still appropriate, or if the |
6b7549d
to
9112ef8
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
im tempted to move validation events after submit events, so we avoid POST_SUBMIT
with prio > 0 running after PRE/POST_VALIDATE with prio 999 still. But that's a BC break also IIUC.
} | ||
|
||
public function validateForm(FormEvent $event) | ||
{ | ||
$form = $event->getForm(); | ||
|
||
if ($form->isRoot()) { | ||
if ($this->eventDispatcher) { | ||
$this->eventDispatcher->dispatch(new PreValidateEvent($event->getForm(), $event->getData()), FormValidationEvents::PRE_VALIDATE); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is there any reason for these events to be root-only?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Validation is only triggered for root forms, hence the checks here. The validator then traverses the form and its data.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
given i have a custom form type listening to VALIDATE events
now, im nesting this form type somewhere else ...all of a sudden the listener isnt called anymore on the child type.
Im not sure that's expected, given validator service maps violations back to underlying form types, so each type has a PRE/POST validation knowledge imho.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, the issue is that this is form validation. Form validation is only triggered on root forms. You always have to attach validation listeners to the form that is being validated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
form validation bubbles down.
put different, why shouldnt i be able to listen to POST_VALIDATE in a child form to check for some synchronized errors?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Form validation does not bubble down. Validation does, in that the form validation metadata contains a Traverse
constraint, which triggers validation for fields, child forms, and the entity. The POST_SUBMIT event for any non-root forms does not trigger validation, which is why there should be no form.(pre|post)_validate event
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hm yes, i agree. Im a bit worried the difference is too subtle, compared to current post-submit with prio >0.
Is there any reason to prefer the new events actually? other then cosmetics.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am kind of interested in this discussion - because my Bundle does exactly this. It works quite nicely.
The most important parts for me - and my project - were - it has to bubble down - because I want to be able to run code inside of child forms whenever the whole form was successfully validated.
And inside of the $event variable, you have to be able to access the current form (current child) and the root form. Otherwise, it would not be possible to detect what data to use for some action like uploading an image or persisting an entity etc.
Like I said. My bundle works like a charm. I like to see this inside the Symfony core (form component).
But if these success metrics are not given, I will stick to my bundle ;).
Anyways. I like to see that you guys put effort into this.
As others have suggested on Slack, the fact that pre/post validation events are triggered during post_submit seems weird. However, any changes to this logic would already constitute a BC break:
Triggering validation in a separate step either before or after post_submit makes it impossible for users to rely on validation being done/not being done when their listener is invoked. Thus, we now give people two additional events to listen to validation without needing to know when during the submit step it is done. If you have other suggestions on how to avoid BC breaks, please let me know. |
we could migrate all current post-submit listeners with prio >0 to post_validate, along with a deprecation it should be configured as such. then in 6.0 post-submit comes free, and as of then it's equivalent to pre_validate instead. Which raises the question if we really need pre-validate. |
@xabbuh Do you time to review this PR? |
{ | ||
$this->validator = $validator; | ||
$this->violationMapper = $violationMapper; | ||
|
||
if (!$this->eventDispatcher) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this be the following?
if (!$this->eventDispatcher) { | |
if (!$eventDispatcher) { |
$this->eventDispatcher
is never set at this point.
Gentlemen, I'd like to discuss another use case for these events -- a use case that is not catered for by this PR, while I think it should be. I'm happy to move it to a new feature request ticket if necessary, however I believe it's best discussed in the context of this PR to avoid potential "change on a recent change". I second what @ro0NL said in his review here: https://github.com/symfony/symfony/pull/37641/files#r460019018. In other words, the POST_VALIDATE has to be dispatched on all children forms recursively, to be useful and not to introduce confusion that @ro0NL mentioned. I realise that the validation is triggered on the root form only (which then recursively validates the entire form tree -- this is done for a very good reason), however that's not a reason for not dispatching the events on the entire form tree. To reiterate what @ro0NL said: given 2 FormTypes: FooType and FooChildType, where FooChildType is one of the child fields on FooType, developers should be able to listen on
Currently, there's no way to achieve the above (without creating new Constraints/ConstraintValidators) because form validation is initiated only on the root form (again, for a good reason). What I propose is that instead of doing the following: // ValidationListener.php
public function validateForm(FormEvent $event)
{
$form = $event->getForm();
if ($form->isRoot()) {
/* ... */
if ($this->eventDispatcher) {
$this->eventDispatcher->dispatch(new PostValidateEvent($event->getForm(), $event->getData()), FormValidationEvents::POST_VALIDATE);
}
}
} the following is done: // ValidationListener.php
public function validateForm(FormEvent $event)
{
$form = $event->getForm();
if ($form->isRoot()) {
/* ... */
/*
* Proof-of-concept code. I didn't run and test it. Just trying to get my point across.
*/
$recursiveFormIterator = new RecursiveIteratorIterator($form, RecursiveIteratorIterator::CHILD_FIRST);
foreach ($recursiveFormIterator as $iteratedForm) {
$iteratedForm->getConfig()->getEventDispatcher()->dispatch(new PostValidateEvent($event->getForm(), $event->getData());
}
}
} Benefits of the solution above over the one in the current PR:
@alcaeus, please let me know what you think of it when you have time. The solution does solve your use case as well. Thank you. |
this will make it even more confusing when explaining the order of |
I agree, stof. In my opinion it is a necessary evil and a natural consequence of the fact that validation is initiated from the root form only. Plus, it is already confusing when a dev discovers that their child form is still not validated when they're attempting to do additional things in a post_submit event (with priority less than 0). I had a solid wtf-moment when I discovered it and I'm sure any dev that attempted to do anything more involved with Symfony Forms can relate to this. By introducing the pre/post-validate events, the framework explicitly says "here's how the things are, stick to these rules and you'll be just fine". To me it's a matter of including proper explanation in the docs that the order of events in the root form is different than in children forms (root: pre-validate, post-submit (at priority 0), post-validate; children: post-submit (at any priority), pre-validate, post-validate), rather than binning the entire feature because it has rough edges. Additionally, I'd say that devs will be more likely to use the post-validate event alone, once it introduced, rather than the post-submit because I personally can't think of any other use case a dev might have to do in a post-submit-like event other than "I want to run this additional, complex checks after individual fields have been validated by the framework". Please re-consider. |
We've just moved away from |
Thanks @fabpot! I've recreated this as #38214 @ro0NL and @d-ph, please raise your questions again in the new PR if they persist so we can continue discussing this PR in one spot.
The new PR is missing a review from maintainers, but good to go otherwise. If the Symfony Team is open to including these events, I'll update the new PR with the rest of the required changes (changelog, deprecations, documentation) |
This PR suggests the introduction of two new events related to form handling:
preValidate
andpostValidate
. These events are dispatched by the validation listener before starting validation and after validation has completed. They allow adding additional handling to the form submit process, e.g. writing changes from a DTO to a managed entity after validation succeeds. Consider the following DTO:Now, a controller can create the DTO, pass it to the form, then store the managed entity:
Of course, this process could be further automated to fit one's needs, but I believe that for the time being, the addition of these events makes is a shortcut towards a better handling of DTOs in forms.