I am working on a symfony 6.4 project in which I have a form ProductType containing 2 fields, both of which are EntityType. Only on the creation of the project, i need to be able to set the forbidden countries based with default values and update the default values based on user input.
->add('series', EntityType::class, [
'label' => 'Collection',
'placeholder' => 'Choisir une collection',
'class' => Series::class,
'choice_label' => 'name',
'query_builder' => function (EntityRepository $repository) use ($product): QueryBuilder {
return $repository->createQueryBuilder('s')
->andWhere('s.brand =' . $product->getBrand()->getId());
}
])
->add('forbiddenCountries', EntityType::class, [
'attr' => [
'class' => 'searchable-select-countries'
],
'data' => $product->getForbiddenCountries(),
'class' => Country::class,
'choice_label' => 'name',
'choice_value' => 'alpha2',
'multiple' => true
])
The forbidden countries are preset like this:
public function updateForbiddenCountries(FormInterface $form, Collection $forbiddenCountries): void
{
$form
->add('forbiddenCountries', EntityType::class, [
'attr' => [
'class' => 'searchable-select-countries'
],
'data' => $forbiddenCountries,
'class' => Country::class,
'choice_label' => 'name',
'choice_value' => 'alpha2',
'multiple' => true
]);
}
->addEventListener(FormEvents::PRE_SET_DATA, function (PreSetDataEvent $event) {
/** @var Product $product */
$product = $event->getData();
if (null !== $product->getId()) {
return;
}
$this->updateForbiddenCountries($event->getForm(), $product->getBrand()->getForbiddenCountries());
})
This first part works properly, here is the expected output:
I also need to be able to change the preset base on the change on the series field. It should look something like this:
->get('series')->addEventListener(FormEvents::POST_SUBMIT, function (PostSubmitEvent $event) {
/** @var Product $product */
$product = $event->getForm()->getParent()->getData();
if (null !== $product->getId()) {
return;
}
/** @var Series $series */
$series = $event->getForm()->getData();
$forbiddenCountries = $series === null ? $product->getBrand()->getForbiddenCountries() : $series->getForbiddenCountries();
dump($forbiddenCountries->toArray());
$this->updateForbiddenCountries($event->getForm()->getParent(), $forbiddenCountries);
})
When i change the series field, i am not getting the expected view although the forbidden countries variable is updated correctly:
As you can see i am expecting to have 3 values instead of 2.
I tried, to add the POST_SUBMIT eventlistener on the form itself (without the ->get('series')
), like this:
->addEventListener(FormEvents::POST_SUBMIT, function (PostSubmitEvent $event) {
/** @var Product $product */
$product = $event->getForm()->getData();
if (null !== $product->getId()) {
return;
}
/** @var Series $series */
$series = $event->getForm()->get('series')->getData();
$product->setSeries($series);
$forbiddenCountries = $series === null ? $product->getBrand()->getForbiddenCountries() : $series->getForbiddenCountries();
$this->updateForbiddenCountries($event->getForm(), $forbiddenCountries);
})
which triggers You cannot add children to a submitted form.
exception
I even tried to update my Object to set the forbidden countries and setData from the event:
->addEventListener(FormEvents::POST_SUBMIT, function (PostSubmitEvent $event) {
/** @var Product $product */
$product = $event->getForm()->getData();
if (null !== $product->getId()) {
return;
}
/** @var Series $series */
$series = $event->getForm()->get('series')->getData();
$product->setSeries($series);
$forbiddenCountries = $series === null ? $product->getBrand()->getForbiddenCountries() : $series->getForbiddenCountries();
foreach ($forbiddenCountries as $forbiddenCountry) {
$product->addForbiddenCountry($forbiddenCountry);
}
$event->setData($product);
})
which would have no impact. Notice here the setData is deprecated.
And trying to set the data on the form itself ($event->getForm()->setData($product);
) would trigger an exception as expected : You cannot change the data of a submitted form.
I am guessing there is a small mistake in what i am doing that i am not seeing. It would be great if i could get some help.
After much troubles and a lot of time with the documentation and different forums, i finally understood that it just cant be done. The problem is that the form being submitted with errors, symfony need to re-display the form's previous state which is why whatever you do to change the data, symfony need to re-display the form's previous state. And this makes sense.
Thus, i decided not to do this with symfony but instead to handle it with an api which would return the next list of elements which i would call in ajax and update the second field with js.
Its not a great solution since we are increasing troubles for maintainance but to do such a thing on the server side would be to handle denormalized data with unmapped fields which you would the normalize yourself which is a lot of work for not much value and it still wont be a good solution.