diff --git a/src/Symfony/Component/Form/Form.php b/src/Symfony/Component/Form/Form.php index 2631faf61863a..0c98022d445b8 100644 --- a/src/Symfony/Component/Form/Form.php +++ b/src/Symfony/Component/Form/Form.php @@ -508,22 +508,24 @@ public function submit($submittedData, bool $clearMissing = true) $dispatcher = $this->config->getEventDispatcher(); // Obviously, a disabled form should not change its data upon submission. - if ($this->isDisabled() && $this->isRoot()) { + if ($this->isDisabled()) { $this->submitted = true; - if ($dispatcher->hasListeners(FormEvents::PRE_SUBMIT)) { - $event = new FormEvent($this, $submittedData); - $dispatcher->dispatch(FormEvents::PRE_SUBMIT, $event); - } + if ($this->isRoot()) { + if ($dispatcher->hasListeners(FormEvents::PRE_SUBMIT)) { + $event = new FormEvent($this, $submittedData); + $dispatcher->dispatch($event, FormEvents::PRE_SUBMIT); + } - if ($dispatcher->hasListeners(FormEvents::SUBMIT)) { - $event = new FormEvent($this, $this->getNormData()); - $dispatcher->dispatch(FormEvents::SUBMIT, $event); - } + if ($dispatcher->hasListeners(FormEvents::SUBMIT)) { + $event = new FormEvent($this, $this->getNormData()); + $dispatcher->dispatch($event, FormEvents::SUBMIT); + } - if ($dispatcher->hasListeners(FormEvents::POST_SUBMIT)) { - $event = new FormEvent($this, $this->getViewData()); - $dispatcher->dispatch(FormEvents::POST_SUBMIT, $event); + if ($dispatcher->hasListeners(FormEvents::POST_SUBMIT)) { + $event = new FormEvent($this, $this->getViewData()); + $dispatcher->dispatch($event, FormEvents::POST_SUBMIT); + } } return $this; diff --git a/src/Symfony/Component/Form/Tests/CompoundFormTest.php b/src/Symfony/Component/Form/Tests/CompoundFormTest.php index 988161953c548..a0eaa0539dc05 100644 --- a/src/Symfony/Component/Form/Tests/CompoundFormTest.php +++ b/src/Symfony/Component/Form/Tests/CompoundFormTest.php @@ -55,7 +55,7 @@ public function testInvalidIfChildIsInvalid() $this->assertFalse($this->form->isValid()); } - public function testDisabledFormsInvalidEvenChildrenInvalid() + public function testDisabledFormIsInvalidIfChildrenInvalid() { $form = $this->getBuilder('person') ->setDisabled(true) @@ -71,6 +71,43 @@ public function testDisabledFormsInvalidEvenChildrenInvalid() $this->assertFalse($form->isValid()); } + public function testUnderlyingObjectCannotChangeOnSubmitIfDisabledForm() + { + $person = new \stdClass(); + $person->name = 'John Doe'; + + $form = $this->getBuilder('person', null, \stdClass::class) + ->setData($person) + ->setDisabled(true) + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->addEventListener(FormEvents::SUBMIT, static function (FormEvent $event) { + $event->getData()->name = 'Jane'; + }) + ->add($this->getBuilder('name')) + ->getForm(); + + $form->submit(['name' => 'Jacques Doe']); + + $this->assertTrue($form->isValid()); + $this->assertSame('John Doe', $person->name); + } + + public function testDisabledChildFormCannotChangeOnSubmit() + { + $form = $this->getBuilder('person') + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->add($this->getBuilder('name')->setDisabled(true)) + ->getForm(); + + $this->assertNull($form->get('name')->getData()); + + $form->submit(['name' => 'Jacques Doe']); + + $this->assertNull($form->get('name')->getData()); + } + public function testSubmitForwardsNullIfNotClearMissingButValueIsExplicitlyNull() { $child = $this->createForm('firstName', false); diff --git a/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorFunctionalTest.php b/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorFunctionalTest.php index 7b724623cd92b..de18a86606724 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorFunctionalTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorFunctionalTest.php @@ -22,11 +22,14 @@ use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormFactoryBuilder; use Symfony\Component\OptionsResolver\OptionsResolver; +use Symfony\Component\Validator\Constraints\Callback; use Symfony\Component\Validator\Constraints\Collection; use Symfony\Component\Validator\Constraints\Expression; +use Symfony\Component\Validator\Constraints\GreaterThanOrEqual; use Symfony\Component\Validator\Constraints\GroupSequence; use Symfony\Component\Validator\Constraints\Length; use Symfony\Component\Validator\Constraints\NotBlank; +use Symfony\Component\Validator\Context\ExecutionContextInterface; use Symfony\Component\Validator\Mapping\ClassMetadata; use Symfony\Component\Validator\Mapping\Factory\LazyLoadingMetadataFactory; use Symfony\Component\Validator\Mapping\Loader\StaticMethodLoader; @@ -386,6 +389,41 @@ function () { $this->assertFalse($form->get('field2')->isValid()); $this->assertCount(1, $form->get('field2')->getErrors()); } + + public function testDisabledFormIsInvalidIfRootFormValidationFails() + { + $form = $this->formFactory + ->createBuilder(FormType::class, ['field1' => 0], [ + 'constraints' => new Callback(['callback' => static function ($data, ExecutionContextInterface $context) { + if ($data['field1'] < 1) { + $context->addViolation('Invalid'); + } + }]), + ]) + ->setDisabled(true) + ->add('field1') + ->getForm(); + + $form->submit(null); + + $this->assertTrue($form->isSubmitted()); + $this->assertFalse($form->isValid()); + $this->assertCount(1, $form->getErrors()); + } + + public function testDisabledFormIsInvalidIfChildrenValidationFails() + { + $form = $this->formFactory->createBuilder(FormType::class, ['field1' => 0]) + ->setDisabled(true) + ->add('field1', null, ['constraints' => new GreaterThanOrEqual(1)]) + ->getForm(); + + $form->submit(null); + + $this->assertTrue($form->isSubmitted()); + $this->assertFalse($form->isValid()); + $this->assertCount(1, $form->get('field1')->getErrors()); + } } class Foo