-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[Form] Introduce validation events #47210
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
base: 7.4
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the Symfony package. | ||
* | ||
* (c) Fabien Potencier <[email protected]> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace Symfony\Component\Form\Extension\Validator\Event; | ||
|
||
use Symfony\Component\Form\FormEvent; | ||
|
||
/** | ||
* This event is dispatched after (root form) validation completes. | ||
*/ | ||
final class PostValidateEvent extends FormEvent | ||
{ | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the Symfony package. | ||
* | ||
* (c) Fabien Potencier <[email protected]> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace Symfony\Component\Form\Extension\Validator\Event; | ||
|
||
use Symfony\Component\Form\FormEvent; | ||
|
||
/** | ||
* This event is dispatched before (root form) validation starts. | ||
*/ | ||
final class PreValidateEvent extends FormEvent | ||
{ | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,9 +13,11 @@ | |
|
||
use Symfony\Component\EventDispatcher\EventSubscriberInterface; | ||
use Symfony\Component\Form\Extension\Validator\Constraints\Form; | ||
use Symfony\Component\Form\Extension\Validator\ValidatorFormEvents; | ||
use Symfony\Component\Form\Extension\Validator\ViolationMapper\ViolationMapperInterface; | ||
use Symfony\Component\Form\FormEvent; | ||
use Symfony\Component\Form\FormEvents; | ||
use Symfony\Component\Form\FormInterface; | ||
use Symfony\Component\Validator\Validator\ValidatorInterface; | ||
|
||
/** | ||
|
@@ -26,6 +28,9 @@ class ValidationListener implements EventSubscriberInterface | |
private ValidatorInterface $validator; | ||
private ViolationMapperInterface $violationMapper; | ||
|
||
/** @var FormInterface[][] */ | ||
private array $dispatchEvents = []; | ||
|
||
public static function getSubscribedEvents(): array | ||
{ | ||
return [FormEvents::POST_SUBMIT => 'validateForm']; | ||
|
@@ -41,7 +46,16 @@ public function validateForm(FormEvent $event) | |
{ | ||
$form = $event->getForm(); | ||
|
||
// Register events to dispatch during (root form) validation | ||
foreach (ValidatorFormEvents::ALIASES as $eventName) { | ||
if ($form->getConfig()->getEventDispatcher()->hasListeners($eventName)) { | ||
$this->dispatchEvents[$eventName][] = $form; | ||
} | ||
} | ||
|
||
if ($form->isRoot()) { | ||
$this->dispatchEvents(ValidatorFormEvents::PRE_VALIDATE); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like the goal of this new event but the dispatching moment is a bit confusing to me. If I create a listener in a nested form type, I expect the event to be received just before the current form is validated in The meaning is different in this case from other events. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To be consistant with POST_VALIDATE event, I decided to dispatch it before the validation of the entire form start. |
||
|
||
// Form groups are validated internally (FormValidator). Here we don't set groups as they are retrieved into the validator. | ||
foreach ($this->validator->validate($form) as $violation) { | ||
// Allow the "invalid" constraint to be put onto | ||
|
@@ -50,6 +64,24 @@ public function validateForm(FormEvent $event) | |
|
||
$this->violationMapper->mapViolation($violation, $form, $allowNonSynchronized); | ||
} | ||
|
||
$this->dispatchEvents(ValidatorFormEvents::POST_VALIDATE); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I understand that child forms can check easily whether their parent is valid or not, but I have the same confusion about the right moment this event is being dispatched. They are not done just after the current form is validated but when the whole validation process is finished for all forms. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the main purpose of this PR, to be able to know if the entire form is valid or not. Maybe we can introduce a 3rd event "VALIDATE" that could be dispatched just after the current form item is validated? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Or rename PRE/POST events by START/FINISH to avoid confusion? |
||
} | ||
} | ||
|
||
private function dispatchEvents(string $eventName) | ||
{ | ||
if (!isset($this->dispatchEvents[$eventName])) { | ||
return; | ||
} | ||
|
||
$event = array_flip(ValidatorFormEvents::ALIASES)[$eventName]; | ||
|
||
foreach ($this->dispatchEvents[$eventName] as $form) { | ||
$event = new $event($form, $form->getData()); | ||
$form->getConfig()->getEventDispatcher()->dispatch($event, $eventName); | ||
Seb33300 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
unset($this->dispatchEvents[$eventName]); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the Symfony package. | ||
* | ||
* (c) Fabien Potencier <[email protected]> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace Symfony\Component\Form\Extension\Validator; | ||
|
||
use Symfony\Component\Form\Extension\Validator\Event\PostValidateEvent; | ||
use Symfony\Component\Form\Extension\Validator\Event\PreValidateEvent; | ||
|
||
final class ValidatorFormEvents | ||
{ | ||
/** | ||
* This event is dispatched before (root form) validation starts. | ||
* | ||
* @Event("Symfony\Component\Form\Extension\Validator\Event\PreValidateEvent") | ||
*/ | ||
public const PRE_VALIDATE = 'form.pre_validate'; | ||
|
||
/** | ||
* This event is dispatched after (root form) validation completes. | ||
* | ||
* @Event("Symfony\Component\Form\Extension\Validator\Event\PostValidateEvent") | ||
*/ | ||
public const POST_VALIDATE = 'form.post_validate'; | ||
|
||
/** | ||
* Event aliases. | ||
* | ||
* These aliases can be consumed by RegisterListenersPass. | ||
*/ | ||
public const ALIASES = [ | ||
PreValidateEvent::class => self::PRE_VALIDATE, | ||
PostValidateEvent::class => self::POST_VALIDATE, | ||
]; | ||
|
||
private function __construct() | ||
{ | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.