-1

I need to create a form adding faculty to the database. First, the user selects a region from the list ( / ChoiceType), then the city of this region from the following list, the university, and finally enters the name of the faculty. The default values are the first region from the database, its first city and first university. Sending the page with default data works, the choice of the region works, but the the choice of the city return to 500 status

Form:

enter image description here

Twig and 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 %}

Form class:

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_parent) {

            if (get_class($entity_parent) === 'App\Entity\Region') {
                if (!$entity_parent->getCities()->count()) {

                    $form->add('city', ChoiceType::class, [
                        'choices' => null,
                        'mapped' => false,
                    ]);
                }
                else {
                    $cities_in_database = $entity_parent->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_parent) === 'App\Entity\City') {
                if (!$entity_parent->getUniversities()->count()) {

                    $form->add('university', ChoiceType::class, [
                        'choices' => null,
                    ]);
                }
                else {
                    $university_in_database = $entity_parent->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');
    }
}

Controller:

/**
* @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_faculty = $this->createForm(InsertFacultyType::class, $faculty, [
        'regions_array' => $regions,
    ]);

    if (!$regions_in_database) {
        $insert_faculty->addError(new FormError("There are no regions!"));
    }

    $insert_faculty->handleRequest($request);
    if ($insert_faculty->isSubmitted() && $insert_faculty->isValid()) {

        $repository = $this->getDoctrine()->getRepository(University::class);
        $faculty_in_database = $repository->findOneBy(
            [
                'name' => $faculty->getName(),
                'university' => $faculty->getUniversity(),
            ]
        );

        if ($faculty_in_database) {
            $insert_faculty->addError(new FormError('Such a faculty is already in the database!'));
        }
        else {
            $faculty->setRating(0);
            if(!$faculty->getUniversity()) {
                $insert_faculty->addError(new FormError("Select the university!"));
            }
            else {
                $entity_manager = $this->getDoctrine()->getManager();
                $entity_manager->persist($faculty);
                $entity_manager->flush();
                $this->addFlash(
                    'success',
                    'Faculty "' . $faculty->getName() . '" successfully saved!'
                );
            }
        }
    }

    return $this->render('admin/insert/faculty/faculty.html.twig', [
        'insert_faculty' => $insert_faculty->createView(),
    ]);
}
Corathir
  • 11
  • 3

1 Answers1

0

I invite you to use the Symfony debug toolbar. It will allow you to see the different problems associated with your code and give much more information about the problems that appear.

Profiler Symfony

About your problem, I think it is necessary to debug at the level of what your form sends to your application. But if you want more help, you must provide the error message that come with your 500 error.

SwissLoop
  • 466
  • 2
  • 11
  • Found a description of the error in the panel. When selecting a city, the program does not refer to the 'city' field, but to the 'region' field, which is located higher in the code. In this regard, the question: "Is the Event Listenner hung on the entire form or only on one field?" Because I hung the Event Listenner after the get('region') call, I thought it was listening to that particular field. – Corathir Aug 18 '20 at 12:15
  • Ok, I took a closer look at your form, wouldn't there be a problem with the static function which allows cities to be hydrated? Are you sure, `$city = $event->getForm()->getData();` is what you want to give to `$city`? – SwissLoop Aug 18 '20 at 12:19
  • I want to get the city that is selected in the list of cities. The line you pointed to was taken from the example in the symfony documentation. The region event listener was written similarly. https://symfony.com/doc/current/form/dynamic_form_modification.html#dynamic-generation-for-submitted-forms – Corathir Aug 18 '20 at 14:16
  • Ok, can you share the full trace of the error? I think only that can help to understand your problem. And, just for a test, if you comment all lines about `$city` is it work well? – SwissLoop Aug 18 '20 at 15:35
  • The problem is that it is passing city data to $builder->get('region')->addEventListener( ... );, but it should be $builder->get('city')->addEventListener( ... ); https://i.stack.imgur.com/4xUVS.png https://drive.google.com/file/d/1VrECRrlvrgDR-pQsFE0iEKHEmgQCmBpc/view?usp=sharing – Corathir Aug 19 '20 at 10:41
  • I just want to know why Symfony treats changing the city field as changing the region field. – Corathir Aug 20 '20 at 12:49