Skip to content

Commit

Permalink
allow using form events as expected with inherit_data option
Browse files Browse the repository at this point in the history
  • Loading branch information
cristoforocervino committed Mar 19, 2021
1 parent c8b48d8 commit 2ae62b6
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 22 deletions.
77 changes: 55 additions & 22 deletions src/Symfony/Component/Form/Form.php
Original file line number Diff line number Diff line change
Expand Up @@ -333,13 +333,18 @@ public function setData($modelData)
}

$this->lockSetData = true;
$dispatcher = $this->config->getEventDispatcher();

// Collecting $this + all children with "inherit_data" option
$thisModelDataAwareForms = $this->getSameModelAwareForms($this);

// Hook to change content of the model data before transformation and mapping children
if ($dispatcher->hasListeners(FormEvents::PRE_SET_DATA)) {
$event = new PreSetDataEvent($this, $modelData);
$dispatcher->dispatch($event, FormEvents::PRE_SET_DATA);
$modelData = $event->getData();
foreach ($thisModelDataAwareForms as $form) {
$dispatcher = $form->getConfig()->getEventDispatcher();
if ($dispatcher->hasListeners(FormEvents::PRE_SET_DATA)) {
$event = new PreSetDataEvent($form, $modelData);
$dispatcher->dispatch($event, FormEvents::PRE_SET_DATA);
$modelData = $event->getData();
}
}

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

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.'".');
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.'".');
}
}

Expand All @@ -374,10 +379,12 @@ public function setData($modelData)
// Update child forms from the data (unless their config data is locked)
$this->config->getDataMapper()->mapDataToForms($viewData, new \RecursiveIteratorIterator(new InheritDataAwareIterator($this->children)));
}

if ($dispatcher->hasListeners(FormEvents::POST_SET_DATA)) {
$event = new PostSetDataEvent($this, $modelData);
$dispatcher->dispatch($event, FormEvents::POST_SET_DATA);
foreach ($thisModelDataAwareForms as $form) {
$dispatcher = $form->getConfig()->getEventDispatcher();
if ($dispatcher->hasListeners(FormEvents::POST_SET_DATA)) {
$event = new PostSetDataEvent($form, $modelData);
$dispatcher->dispatch($event, FormEvents::POST_SET_DATA);
}
}

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

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

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

// Hook to change content of the data submitted by the browser
if ($dispatcher->hasListeners(FormEvents::PRE_SUBMIT)) {
$event = new PreSubmitEvent($this, $submittedData);
$dispatcher->dispatch($event, FormEvents::PRE_SUBMIT);
$submittedData = $event->getData();
foreach ($thisModelDataAwareForms as $form) {
$dispatcher = $form->getConfig()->getEventDispatcher();
if ($dispatcher->hasListeners(FormEvents::PRE_SUBMIT)) {
$event = new PreSubmitEvent($form, $submittedData);
$dispatcher->dispatch($event, FormEvents::PRE_SUBMIT);
$submittedData = $event->getData();
}
}

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

// Hook to change content of the data in the normalized
// representation
if ($dispatcher->hasListeners(FormEvents::SUBMIT)) {
$event = new SubmitEvent($this, $normData);
$dispatcher->dispatch($event, FormEvents::SUBMIT);
$normData = $event->getData();
foreach ($thisModelDataAwareForms as $form) {
$dispatcher = $form->getConfig()->getEventDispatcher();
if ($dispatcher->hasListeners(FormEvents::SUBMIT)) {
$event = new SubmitEvent($form, $normData);
$dispatcher->dispatch($event, FormEvents::SUBMIT);
$normData = $event->getData();
}
}

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

if ($dispatcher->hasListeners(FormEvents::POST_SUBMIT)) {
$event = new PostSubmitEvent($this, $viewData);
$dispatcher->dispatch($event, FormEvents::POST_SUBMIT);
foreach ($thisModelDataAwareForms as $form) {
$dispatcher = $form->getConfig()->getEventDispatcher();
if ($dispatcher->hasListeners(FormEvents::POST_SUBMIT)) {
$event = new PostSubmitEvent($form, $viewData);
$dispatcher->dispatch($event, FormEvents::POST_SUBMIT);
}
}

return $this;
}

/**
* @return array<FormInterface>
*/
private function getSameModelAwareForms(self $form): array
{
return array_reduce(iterator_to_array($form->children),
static function (array $children, FormInterface $child): array {
if ($child->getConfig()->getInheritData()) {
$children[] = $child;
}

return $children;
}, [$form]
);
}

/**
* {@inheritdoc}
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

use Symfony\Component\Form\CallbackTransformer;
use Symfony\Component\Form\DataMapperInterface;
use Symfony\Component\Form\Event\PostSubmitEvent;
use Symfony\Component\Form\Exception\InvalidArgumentException;
use Symfony\Component\Form\Exception\LogicException;
use Symfony\Component\Form\Exception\TransformationFailedException;
Expand All @@ -23,6 +24,8 @@
use Symfony\Component\Form\Form;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormError;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\Forms;
use Symfony\Component\Form\Tests\Fixtures\Author;
use Symfony\Component\Form\Tests\Fixtures\FixedDataTransformer;
Expand Down Expand Up @@ -836,6 +839,51 @@ public function testFormAttrAsStringWithNoId()
$this->assertSame($view->vars['id'], $view['child1']->vars['attr']['form']);
$this->assertSame($view->vars['id'], $view['child2']->vars['attr']['form']);
}

public function testSetDataEventsDispatchedToChildrenWithInheritDataConfigured()
{
$data = ['child' => 'child_value'];
$calledEvents = [];
$form = $this->factory->createNamedBuilder('form', self::TESTED_TYPE, $data)
->add(
$this->factory->createNamedBuilder('inherit_data_type', self::TESTED_TYPE, null, [
'inherit_data' => true,
])
->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) use (&$calledEvents) {
$calledEvents[] = FormEvents::PRE_SET_DATA;
$event->getForm()->add('child', self::TESTED_TYPE);
})
->addEventListener(FormEvents::POST_SET_DATA, function (FormEvent $event) use (&$calledEvents) {
$calledEvents[] = FormEvents::POST_SET_DATA;
$event->getForm()->add('child2', self::TESTED_TYPE, ['data' => $event->getData()['child']]);
})
->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) use (&$calledEvents) {
$calledEvents[] = FormEvents::PRE_SUBMIT;
$this->assertSame($event->getData(), $event->getForm()->getData());
})
->addEventListener(FormEvents::SUBMIT, function (FormEvent $event) use (&$calledEvents) {
$calledEvents[] = FormEvents::SUBMIT;
$this->assertNotNull($event->getData());
})
->addEventListener(FormEvents::POST_SUBMIT, function (PostSubmitEvent $event) use (&$calledEvents) {
$calledEvents[] = FormEvents::POST_SUBMIT;
$this->assertNotNull($event->getData());
})
)
->getForm();
$this->assertTrue($form->get('inherit_data_type')->has('child'));
$this->assertTrue($form->get('inherit_data_type')->has('child2'));
$this->assertSame('child_value', $form->get('inherit_data_type')->get('child')->getData());
$this->assertSame('child_value', $form->get('inherit_data_type')->get('child2')->getData());
$errorMessage = '"%s" event has not been called on form child with "inherit_data" option.';
$form->submit($data);
$this->assertContains(FormEvents::PRE_SET_DATA, $calledEvents, sprintf($errorMessage, FormEvents::PRE_SET_DATA));
$this->assertContains(FormEvents::POST_SET_DATA, $calledEvents, sprintf($errorMessage, FormEvents::POST_SET_DATA));
$this->assertContains(FormEvents::PRE_SUBMIT, $calledEvents, sprintf($errorMessage, FormEvents::PRE_SUBMIT));
$this->assertContains(FormEvents::SUBMIT, $calledEvents, sprintf($errorMessage, FormEvents::SUBMIT));
$this->assertContains(FormEvents::POST_SUBMIT, $calledEvents, sprintf($errorMessage, FormEvents::POST_SUBMIT));
$this->assertCount(5, $calledEvents);
}
}

class Money
Expand Down

0 comments on commit 2ae62b6

Please sign in to comment.