Skip to content

Commit adbecf2

Browse files
allow *_SET_DATA to be used with inherit_data option
allow using form events as expected with inherit_data option
1 parent c8b48d8 commit adbecf2

File tree

2 files changed

+103
-22
lines changed

2 files changed

+103
-22
lines changed

src/Symfony/Component/Form/Form.php

Lines changed: 55 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -333,13 +333,18 @@ public function setData($modelData)
333333
}
334334

335335
$this->lockSetData = true;
336-
$dispatcher = $this->config->getEventDispatcher();
336+
337+
// Collecting $this + all children with "inherit_data" option
338+
$thisModelDataAwareForms = $this->getSameModelAwareForms($this);
337339

338340
// Hook to change content of the model data before transformation and mapping children
339-
if ($dispatcher->hasListeners(FormEvents::PRE_SET_DATA)) {
340-
$event = new PreSetDataEvent($this, $modelData);
341-
$dispatcher->dispatch($event, FormEvents::PRE_SET_DATA);
342-
$modelData = $event->getData();
341+
foreach ($thisModelDataAwareForms as $form) {
342+
$dispatcher = $form->getConfig()->getEventDispatcher();
343+
if ($dispatcher->hasListeners(FormEvents::PRE_SET_DATA)) {
344+
$event = new PreSetDataEvent($form, $modelData);
345+
$dispatcher->dispatch($event, FormEvents::PRE_SET_DATA);
346+
$modelData = $event->getData();
347+
}
343348
}
344349

345350
// Treat data as strings unless a transformer exists
@@ -359,7 +364,7 @@ public function setData($modelData)
359364
if (null !== $dataClass && !$viewData instanceof $dataClass) {
360365
$actualType = get_debug_type($viewData);
361366

362-
throw new LogicException('The form\'s view data is expected to be a "'.$dataClass.'", but it is a "'.$actualType.'". You can avoid this error by setting the "data_class" option to null or by adding a view transformer that transforms "'.$actualType.'" to an instance of "'.$dataClass.'".');
367+
throw new LogicException('The form\'s view data is expected to be a ".'.$dataClass.'", but it is a "'.$actualType.'". You can avoid this error by setting the "data_class" option to null or by adding a view transformer that transforms "'.$actualType.'" to an instance of "'.$dataClass.'".');
363368
}
364369
}
365370

@@ -374,10 +379,12 @@ public function setData($modelData)
374379
// Update child forms from the data (unless their config data is locked)
375380
$this->config->getDataMapper()->mapDataToForms($viewData, new \RecursiveIteratorIterator(new InheritDataAwareIterator($this->children)));
376381
}
377-
378-
if ($dispatcher->hasListeners(FormEvents::POST_SET_DATA)) {
379-
$event = new PostSetDataEvent($this, $modelData);
380-
$dispatcher->dispatch($event, FormEvents::POST_SET_DATA);
382+
foreach ($thisModelDataAwareForms as $form) {
383+
$dispatcher = $form->getConfig()->getEventDispatcher();
384+
if ($dispatcher->hasListeners(FormEvents::POST_SET_DATA)) {
385+
$event = new PostSetDataEvent($form, $modelData);
386+
$dispatcher->dispatch($event, FormEvents::POST_SET_DATA);
387+
}
381388
}
382389

383390
return $this;
@@ -538,7 +545,8 @@ public function submit($submittedData, bool $clearMissing = true)
538545
$this->transformationFailure = new TransformationFailedException('Submitted data was expected to be text or number, array given.');
539546
}
540547

541-
$dispatcher = $this->config->getEventDispatcher();
548+
// Collecting $this + all children with "inherit_data" option only if $this is not using "inherit_data" option
549+
$thisModelDataAwareForms = !$this->getConfig()->getInheritData() ? $this->getSameModelAwareForms($this) : [];
542550

543551
$modelData = null;
544552
$normData = null;
@@ -550,10 +558,13 @@ public function submit($submittedData, bool $clearMissing = true)
550558
}
551559

552560
// Hook to change content of the data submitted by the browser
553-
if ($dispatcher->hasListeners(FormEvents::PRE_SUBMIT)) {
554-
$event = new PreSubmitEvent($this, $submittedData);
555-
$dispatcher->dispatch($event, FormEvents::PRE_SUBMIT);
556-
$submittedData = $event->getData();
561+
foreach ($thisModelDataAwareForms as $form) {
562+
$dispatcher = $form->getConfig()->getEventDispatcher();
563+
if ($dispatcher->hasListeners(FormEvents::PRE_SUBMIT)) {
564+
$event = new PreSubmitEvent($form, $submittedData);
565+
$dispatcher->dispatch($event, FormEvents::PRE_SUBMIT);
566+
$submittedData = $event->getData();
567+
}
557568
}
558569

559570
// Check whether the form is compound.
@@ -636,10 +647,13 @@ public function submit($submittedData, bool $clearMissing = true)
636647

637648
// Hook to change content of the data in the normalized
638649
// representation
639-
if ($dispatcher->hasListeners(FormEvents::SUBMIT)) {
640-
$event = new SubmitEvent($this, $normData);
641-
$dispatcher->dispatch($event, FormEvents::SUBMIT);
642-
$normData = $event->getData();
650+
foreach ($thisModelDataAwareForms as $form) {
651+
$dispatcher = $form->getConfig()->getEventDispatcher();
652+
if ($dispatcher->hasListeners(FormEvents::SUBMIT)) {
653+
$event = new SubmitEvent($form, $normData);
654+
$dispatcher->dispatch($event, FormEvents::SUBMIT);
655+
$normData = $event->getData();
656+
}
643657
}
644658

645659
// Synchronize representations - must not change the content!
@@ -663,14 +677,33 @@ public function submit($submittedData, bool $clearMissing = true)
663677
$this->normData = $normData;
664678
$this->viewData = $viewData;
665679

666-
if ($dispatcher->hasListeners(FormEvents::POST_SUBMIT)) {
667-
$event = new PostSubmitEvent($this, $viewData);
668-
$dispatcher->dispatch($event, FormEvents::POST_SUBMIT);
680+
foreach ($thisModelDataAwareForms as $form) {
681+
$dispatcher = $form->getConfig()->getEventDispatcher();
682+
if ($dispatcher->hasListeners(FormEvents::POST_SUBMIT)) {
683+
$event = new PostSubmitEvent($form, $viewData);
684+
$dispatcher->dispatch($event, FormEvents::POST_SUBMIT);
685+
}
669686
}
670687

671688
return $this;
672689
}
673690

691+
/**
692+
* @return array<FormInterface>
693+
*/
694+
private function getSameModelAwareForms(self $form): array
695+
{
696+
return array_reduce(iterator_to_array($form->children),
697+
static function (array $children, FormInterface $child): array {
698+
if ($child->getConfig()->getInheritData()) {
699+
$children[] = $child;
700+
}
701+
702+
return $children;
703+
}, [$form]
704+
);
705+
}
706+
674707
/**
675708
* {@inheritdoc}
676709
*/

src/Symfony/Component/Form/Tests/Extension/Core/Type/FormTypeTest.php

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use Symfony\Component\Form\CallbackTransformer;
1515
use Symfony\Component\Form\DataMapperInterface;
16+
use Symfony\Component\Form\Event\PostSubmitEvent;
1617
use Symfony\Component\Form\Exception\InvalidArgumentException;
1718
use Symfony\Component\Form\Exception\LogicException;
1819
use Symfony\Component\Form\Exception\TransformationFailedException;
@@ -23,6 +24,8 @@
2324
use Symfony\Component\Form\Form;
2425
use Symfony\Component\Form\FormBuilderInterface;
2526
use Symfony\Component\Form\FormError;
27+
use Symfony\Component\Form\FormEvent;
28+
use Symfony\Component\Form\FormEvents;
2629
use Symfony\Component\Form\Forms;
2730
use Symfony\Component\Form\Tests\Fixtures\Author;
2831
use Symfony\Component\Form\Tests\Fixtures\FixedDataTransformer;
@@ -836,6 +839,51 @@ public function testFormAttrAsStringWithNoId()
836839
$this->assertSame($view->vars['id'], $view['child1']->vars['attr']['form']);
837840
$this->assertSame($view->vars['id'], $view['child2']->vars['attr']['form']);
838841
}
842+
843+
public function testSetDataEventsDispatchedToChildrenWithInheritDataConfigured()
844+
{
845+
$data = ['child' => 'child_value'];
846+
$calledEvents = [];
847+
$form = $this->factory->createNamedBuilder('form', self::TESTED_TYPE, $data)
848+
->add(
849+
$this->factory->createNamedBuilder('inherit_data_type', self::TESTED_TYPE, null, [
850+
'inherit_data' => true,
851+
])
852+
->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) use (&$calledEvents) {
853+
$calledEvents[] = FormEvents::PRE_SET_DATA;
854+
$event->getForm()->add('child', self::TESTED_TYPE);
855+
})
856+
->addEventListener(FormEvents::POST_SET_DATA, function (FormEvent $event) use (&$calledEvents) {
857+
$calledEvents[] = FormEvents::POST_SET_DATA;
858+
$event->getForm()->add('child2', self::TESTED_TYPE, ['data' => $event->getData()['child']]);
859+
})
860+
->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) use (&$calledEvents) {
861+
$calledEvents[] = FormEvents::PRE_SUBMIT;
862+
$this->assertSame($event->getData(), $event->getForm()->getData());
863+
})
864+
->addEventListener(FormEvents::SUBMIT, function (FormEvent $event) use (&$calledEvents) {
865+
$calledEvents[] = FormEvents::SUBMIT;
866+
$this->assertNotNull($event->getData());
867+
})
868+
->addEventListener(FormEvents::POST_SUBMIT, function (PostSubmitEvent $event) use (&$calledEvents) {
869+
$calledEvents[] = FormEvents::POST_SUBMIT;
870+
$this->assertNotNull($event->getData());
871+
})
872+
)
873+
->getForm();
874+
$this->assertTrue($form->get('inherit_data_type')->has('child'));
875+
$this->assertTrue($form->get('inherit_data_type')->has('child2'));
876+
$this->assertSame('child_value', $form->get('inherit_data_type')->get('child')->getData());
877+
$this->assertSame('child_value', $form->get('inherit_data_type')->get('child2')->getData());
878+
$errorMessage = '"%s" event has not been called on form child with "inherit_data" option.';
879+
$form->submit($data);
880+
$this->assertContains(FormEvents::PRE_SET_DATA, $calledEvents, sprintf($errorMessage, FormEvents::PRE_SET_DATA));
881+
$this->assertContains(FormEvents::POST_SET_DATA, $calledEvents, sprintf($errorMessage, FormEvents::POST_SET_DATA));
882+
$this->assertContains(FormEvents::PRE_SUBMIT, $calledEvents, sprintf($errorMessage, FormEvents::PRE_SUBMIT));
883+
$this->assertContains(FormEvents::SUBMIT, $calledEvents, sprintf($errorMessage, FormEvents::SUBMIT));
884+
$this->assertContains(FormEvents::POST_SUBMIT, $calledEvents, sprintf($errorMessage, FormEvents::POST_SUBMIT));
885+
$this->assertCount(5, $calledEvents);
886+
}
839887
}
840888

841889
class Money

0 commit comments

Comments
 (0)