Answer the question
In order to leave comments, you need to log in
How to create Symfony 2 cascading form?
Good day everyone. There is a task: to make a symfony form with three fields Country, Region, City.
These data are selected from drop-down lists. Accordingly, the options in the lists Region and City. appear only after selection in the previous field.
From the JS side, everything is clear and works, but when submitting the form, there is a gag, namely:
Everything is implemented as indicated here: symfony.com.ua/doc/current/form/dynamic_form_modif... Section "Dynamic generation for the submitted form". And with the "Region" field, there are no questions - everything works, but the POST_SUBMIT event for the region does not work.
Here is the fom code:
<?php
class MyInputType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
/** @var MyInput $data */
$data = $builder->getData();
$builder->add('country', 'entity', [
'class' => Country::class,
'choices' => $data->getAvailableCountries(),
'required' => false,
]);
$this->addRegionPole($builder,$data->getAvailableRegions($data->getCountry()));
$this->addCityPole($builder,$data->getAvailableCities($data->getCountry(), $data->getRegion()));
$this->iniPOST_SUBMIT_event($builder);
}
private function iniPOST_SUBMIT_event(FormBuilderInterface $builder)
{
$context = $this;
$builder->get('country')->addEventListener(
FormEvents::POST_SUBMIT,
function (FormEvent $event) use ($context) {
/** @var Country $data */
$data = $event->getForm()->getData();
$form = $event->getForm()->getParent();
$context->addRegionPole($form,$form->getData()->getAvailableRegions($data));
}
);
$builder->get('region')->addEventListener(
FormEvents::POST_SUBMIT,
function (FormEvent $event) use ($context) {
/** @var Region $data */
$region = $event->getForm()->getData();
$form = $event->getForm()->getParent();
/** @var Country $country */
$country = $form->get('country')->getData();
/** @var MyInput $MyInput */
$MyInput = $form->getData();
$context->addCityPole($form,$MyInput->getAvailableCities($country,$region));
}
);
}
/**
* @param FormInterface $form
* @param array $choices
*/
protected function addRegionPole($form, array $choices)
{
$form->add('region', 'entity', [
'class' => Region::class,
'choices' => $choices,
'required' => false,
]);
}
/**
* @param FormInterface | FormBuilderInterface $form
* @param array $choices
*/
protected function addCityPole($form, array $choices)
{
$form->add('city', 'entity', [
'class' => City::class,
'choices' => $choices,
'required' => false,
]);
}
/**
* @param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults([
'data_class' => MyInput::class,
'csrf_protection' => false,
]);
}
/**
* Returns the name of this type.
*
* @return string The name of this type
*/
public function getName()
{
return 'MyInputType';
}
}
Answer the question
In order to leave comments, you need to log in
Everything is much simpler and of course there are a lot of approaches.
The classic approach does NOT imply js and adds new fields after each form submission, the solution is extremely simple:
<?php
interface ObjectInterface {
public function getCountry(): ?Country;
public function getRegion(): ?Region;
}
class AppType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('country', EntityType::class, [
'class' => \App\Entity\Country::class,
])
;
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
/** @var ObjectInterface $object */
if (null === $object = $event->getData()) {
return;
}
$form = $event->getForm();
if ($object->getCountry()) {
$form->add('region', EntityType::class, [
'class' => \App\Entity\Region::class,
'query_builder' => function (EntityRepository $repository) use ($object) {
return $repository->createQueryBuilder('e')->where('e.country = :country')->setParameter('country', $object->getCountry());
}
]);
}
if ($object->getRegion()) {
$form->add('city', EntityType::class, [
'class' => \App\Entity\City::class,
'query_builder' => function (EntityRepository $repository) use ($object) {
return $repository->createQueryBuilder('e')->where('e.region = :region')->setParameter('region', $object->getRegion());
}
]);
}
});
}
}
You are dynamically redefining the field for the region, thereby "overwriting" the event listener. Those. you need to add the listener again:
$builder->get('country')->addEventListener(
FormEvents::POST_SUBMIT,
function (FormEvent $event) use ($context) {
/** @var Country $data */
$data = $event->getForm()->getData();
$form = $event->getForm()->getParent();
// !!! Здесь слушатель события для поля 'region' удаляется !!!
$context->addRegionPole($form,$form->getData()->getAvailableRegions($data));
}
);
Didn't find what you were looking for?
Ask your questionAsk a Question
731 491 924 answers to any question