Как создать каскадную форму Symfony 2?
Всё намного проще и само собой подходов куча.
Классический подход НЕ подразумевает js и добавляет новые поля после каждого сабмита формы, решается крайне просто:<?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());
                    }
                ]);
            }
        });
    }
}
Цена вопроса 3 POST запроса
Но такой подход не удобен и только путает.
Как решить?
Одним из способов будет являться следующее:
- Создаём контроллер с методами получения регионов, городов.
- На фронте любыми способами рисуем инпут
- Динамически (на фронте) делаем с этими формами что угодно
- Отправляем форму
В этом случае symfony форма будет выглядить примитивно$builder->add('country')->add('region')->add('city');
Надо будет только задать группу валидации, где вы спокойненько провалидуриете всё дерево, начиная со страны.
Цена вопроса - 2 ajax запроса, 1 POST
Или...
Рисуем форму$builder->add('country')->add('region')->add('city');
Где region и city - ChoiceType пустые.
Аяксом забираем с сервера регионы, а затем города.
В событии формы (SUBMIT и POST_SUBMIT с приоритетом нижк валидатора) ручками ссетим эти данные.
Лично мне предпочтителен первый подход (классика без js), на которую потом уже навешиваются плюшки. 
Запросов больше, но объект собирается, как на фронте, так и на бэке последовательно и нет никаких проблем ни с редактированием, ни с созданием.
Для понимания. PRE_SET_DATA - это GET запрос, прим перед резолвом данных (имеено по этому в коде проверка на null). PRE_SUBMIT - это POST запрос. В обоих случаях у вас объект в событии.
Соответственно за последовательность отвечает data_class, объект DTO (или сущнсоть) в котором будет страна, регион и город.
Бонусом (разобравшись с этой ересью) - валидаторы, всякие ресты и сериализаторы прикручиваются к таким формам на раз-два по необходимости.
> Пробовал - не работает. В него не попадают значения динамических полей
Отвечу на ваш коммент тут.
Во-первых - событие PRE_SET_DATA с проверкой данных на null
Во-вторых - событие PRE_SUBMIT (пожеланию) на биндинг данных (если у вас в форме не используется сущности)
symfony.com/doc/current/form/dynamic_form_modifica...
Вы динамически переопределяете поле для региона, тем самым "затирая" слушатель события. Т.е. вам нужно еще раз добавить слушатель:$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));
            }
        );
Возможно, вам лучше вынести код с навешиванием обработчиков в методы addRegionPole и addCityPole: