Сложная (многоуровневая) динамическая форма в Symfony

Мне нужно создать форму добавления факультета в базу данных. Сначала пользователь выбирает регион из списка (<select> / ChoiceType), затем город этого региона из следующего списка, выбирает университет и, наконец, вводит название факультета. Значения по умолчанию - это первый регион из базы данных, его первый город и первый университет. Отправка страницы с данными по умолчанию работает, выбор региона работает, но выбор города возвращает статус 500. Из описания ошибки следует, что на изменение города реагирует слушатель $builder->get('region')->addEventListener(), а не $builder->get('city')->addEventListener(). Как это исправить?

Форма:

Form

Ошибка:

Error1 Error2

Twig и ajax:

{% extends 'admin/insert/insert.html.twig' %}

{% block title %}Add Faculty{% endblock %}

{% block body %}
    <div class="insert">

        <h1 class="insert__title">Add Faculty</h1>

        {{ form_start(insert_faculty, { 'attr' : {'class' : 'insert__form'} }) }}

        {% for message in app.flashes('success') %}
            <div class="insert__success">
                {{ message }}
            </div>
        {% endfor %}

        <div class="insert__errors">
            {{ form_errors(insert_faculty) }}
        </div>

        {{ form_label(insert_faculty.region, 'Region:', { 'label_attr' : {'class' : 'insert__label'} }) }}
        {{ form_widget(insert_faculty.region, { 'attr' : {'class' : 'insert__input'} }) }}

        {{ form_label(insert_faculty.city, 'City:', { 'label_attr' : {'class' : 'insert__label'} }) }}
        {{ form_widget(insert_faculty.city, { 'attr' : {'class' : 'insert__input'} }) }}

        {{ form_label(insert_faculty.university, 'University:', { 'label_attr' : {'class' : 'insert__label'} }) }}
        {{ form_widget(insert_faculty.university, { 'attr' : {'class' : 'insert__input'} }) }}

        {{ form_label(insert_faculty.name, 'Name:', { 'label_attr' : {'class' : 'insert__label'} }) }}
        {{ form_widget(insert_faculty.name, { 'attr' : {'class' : 'insert__input insert__input_name'} }) }}

        <button type="submit" class="insert__button">Save</button>

        {{ form_end(insert_faculty) }}

        <div class="insert__buttons">
            <a href="{{ path('insert') }}" class="insert__button">Back</a>
        </div>
    </div>

    {%  block javascripts_footer %}
        {{ parent() }}
        <script>
            let $region = $('#insert_faculty_region');
            $region.change(function() {
                let $form = $(this).closest('form');
                let data = {};
                data[$region.attr('name')] = $region.val();
                $.ajax({
                    url : $form.attr('action'),
                    type: $form.attr('method'),
                    data : data,
                    success: function(get) {
                        $('#insert_faculty_city').html(
                            $(get).find('#insert_faculty_city').html()
                        );
                        $('#insert_faculty_university').html(
                            $(get).find('#insert_faculty_university').html()
                        );
                    }
                });
            });

            let $city = $('#insert_faculty_city');
            $city.change(function() {
                let $form = $(this).closest('form');
                let data = {};
                data[$city.attr('name')] = $city.val();
                $.ajax({
                    url : $form.attr('action'),
                    type: $form.attr('method'),
                    data : data,
                    success: function(get) {
                        $('#insert_faculty_university').html(
                            $(get).find('#insert_faculty_university').html()
                        );
                    }
                });
            });
        </script>
    {% endblock %}
{% endblock %}

Класс формы:

class InsertFacultyType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            // Список регионов
            ->add('region', ChoiceType::class, [
                'choices'  => $options['regions_array'],
                'mapped' => false,
            ])
            // Список городов
            ->add('city', ChoiceType::class, [
                'choices'  => null,
                'mapped' => false,
            ])
            // Список вузов
            ->add('university', ChoiceType::class, [
                'choices'  => null,
            ])
            // Название факультета
            ->add('name')
        ;

        // Функция изменения формы
        $formModifier = function (FormInterface $form, $entity) {

            // Если изменён регион
            if (get_class($entity) === 'App\Entity\Region') {
                // Если у него нет городов, отправляем null
                if (!$entity->getCities()->count()) {

                    $form->add('city', ChoiceType::class, [
                        'choices' => null,
                        'mapped' => false,
                    ]);
                }
                else {
                    // Формируем массив, где ключ (<option>) - имя города, значение - сам город
                    $cities_in_database = $entity->getCities();
                    foreach ($cities_in_database as $city) {
                        $cities[$city->getName()] = $city;
                    }

                    // Обновляем поле формы
                    $form->add('city', ChoiceType::class, [
                        'choices' => $cities,
                        'mapped' => false,
                    ]);
                }
            }
            // Если изменён регион
            else if (get_class($entity) === 'App\Entity\City') {
                // Если у него нет вузов, отправляем null
                if (!$entity->getUniversities()->count()) {

                    $form->add('university', ChoiceType::class, [
                        'choices' => null,
                    ]);
                }
                else {
                    // Формируем массив, где ключ (<option>) - имя вуза, значение - сам вуз
                    $university_in_database = $entity->getUniversities();
                    foreach ($university_in_database as $university) {
                        $universities[$university->getName()] = $university;
                    }

                    // Обновляем поле формы
                    $form->add('university', ChoiceType::class, [
                        'choices' => $universities,
                    ]);
                }
            }
        };

        // Начальные данные
        $builder->addEventListener(
            FormEvents::PRE_SET_DATA,
            function (FormEvent $event) use ($options, $formModifier, $builder) {
                $region = $options['regions_array'][array_key_first($options['regions_array'])];
                $city = $region->getCities()[0];
                $formModifier($event->getForm(), $region);
                $formModifier($event->getForm(), $city);
            }
        );

        // Изменение региона
        $builder->get('region')->addEventListener(
            FormEvents::POST_SUBMIT,
            function (FormEvent $event) use ($formModifier) {
                $region = $event->getForm()->getData();
                $city = $region->getCities()[0];

                $formModifier($event->getForm()->getParent(), $region);
                $formModifier($event->getForm()->getParent(), $city);
            }
        );

        // Изменение города
        $builder->get('city')->addEventListener(
            FormEvents::POST_SUBMIT,
            function (FormEvent $event) use ($formModifier) {
                $city = $event->getForm()->getData();

                $formModifier($event->getForm()->getParent(), $city);
            }
        );
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => Faculty::class,
            'regions_array' => null,
        ]);

        $resolver->setAllowedTypes('regions_array', 'array');
    }
}

Контроллер:

/**
 * @Route("/admin/insert/faculty", name="faculty")
 */
public function faculty(Request $request)
{
    // Получаем список регионов
    $regions_in_database = $this->getDoctrine()->getRepository(Region::class)->findAll();

    // Записываем их в массив, где индекс - имя региона
    $regions = [];
    foreach ($regions_in_database as $region) {
        $regions[(string)$region->getName()] = $region;
    }

    // Создаём пустой класс факультета
    $faculty = new Faculty();
    // и форму под него
    $insert_form = $this->createForm(InsertFacultyType::class, $faculty, [
        'regions_array' => $regions,
    ]);

    // Проверяем наличие регионов
    if (!$regions_in_database) {
        $insert_form->addError(new FormError("Отсутствуют регионы!
            Пожалуйста, перейдите по ссылке /insert/region и добавьте соответствующий регион"));
    }

    // Отправка формы
    $insert_form->handleRequest($request);
    if ($insert_form->isSubmitted() && $insert_form->isValid()) {

        // Проверяем, есть ли уже такой факультет в базе
        $repository = $this->getDoctrine()->getRepository(University::class);
        $faculty_in_database = $repository->findOneBy(
            [
                'name' => $faculty->getName(),
                'university' => $faculty->getUniversity(),
            ]
        );

        // Если есть, выдаём ошибку
        if ($faculty_in_database) {
            $insert_form->addError(new FormError('Такой факультет уже есть в базе!'));
        }
        else {
            $faculty->setRating(0);
            // Проверяем, был ли отправлен университет
            if(!$faculty->getUniversity()) {
                $insert_form->addError(new FormError("Укажите университет!"));
            }
            // Сохраняем факультет в базу
            else {
                $entity_manager = $this->getDoctrine()->getManager();
                $entity_manager->persist($faculty);
                $entity_manager->flush();
                $this->addFlash(
                    'success',
                    'Факультет "' . $faculty->getName() . '" успешно сохранён!'
                );
            }
        }
    }

    return $this->render('admin/insert/faculty/faculty.html.twig', [
        'insert_form' => $insert_form->createView(),
    ]);
}

Ответы (0 шт):