1

Using PhpStorm.2016.3.2 with Symfony3 on Ubuntu16.04

I have a FormType.php related to an entity called Restaurant.

Inside this entity, I have a foreign key called city_id connected with an entity called city. So in my FormType I actually succeeded in calling my city entity with EntityType::class like that:

->add('city', EntityType::class, array(
            'class'                 => 'AppBundle:City',
            'choice_label'          => 'name',
        ))

This actually works and I have a button appearing on my page where I can select my cities from my database.

enter image description here

But I encountered a slight problem with that. What if my user wants to add a new city like Amsterdam as an example?

How can I do that in my FormType.php with my entity city?

2

1 Answer 1

2

I advise you to use an event listener/subscriber in your form type. You can look at how to dynamically modify forms using form events.

How to proceed : When a user select a particular option in your select field called "city" then a listener/subscriber adds a text field to let the user write a city name.

So, in your form type, you can add an event subscriber like that :

$builder->addEventSubscriber(new CityFieldSubscriber($builder->getFormFactory(), 'city', $options['manager']);

Note that you need to pass an entity manager as an option of your form type.

Then create the subscriber (this is an example. I have not executed this code so there are probably errors) :

class CityFieldSubscriber implements EventSubscriberInterface
{
    protected $factory;
    protected $fieldName;
    protected $listenedFieldName;
    protected $repository;
    protected $optionId;

    public function __construct(FormFactoryInterface $factory, ObjectManager $manager)
    {
        $this->factory              = $factory;
        $this->fieldName            = 'cityName'; // text field to add
        $this->listenedFieldName    = 'city'; // entity field to listen
        $this->repository           = $manager->getRepository('AppBundle:City');
        $this->optionId             = 0; // The value of the option in your select field
    }

    public static function getSubscribedEvents()
    {
        return [
            FormEvents::POST_SET_DATA   => 'postSetData',
            FormEvents::PRE_SUBMIT      => 'preSubmit'
        ];
    }

    private function addCityForm(FormInterface $form, City $city = null)
    {
        if (null !== $city) {
            if ($city->getId() === $this->optionId) {
                $form->add($this->fieldName, TextType::class, [
                    'label' => 'app.form.type.cityName.label',
                    'required' => true
                ]);
            }
        }
    }

    public function postSetData(FormEvent $event)
    {
        $city = $event->getForm()->get($this->listenedFieldName)->getData();

        if ($city) {
            $this->addCityForm($event->getForm(), $city);
        } else {
            $this->addCityForm($event->getForm());
        }
    }

    public function preSubmit(FormEvent $event)
    {
        $data = $event->getData();

        if (isset($data[$this->listenedFieldName]) && $data[$this->listenedFieldName] !== '') {
            $this->addCityForm($event->getForm(), $this->repository->find($data[$this->listenedFieldName]));
        }

        if (!isset($data[$this->listenedFieldName])) {
            $this->addCityForm($event->getForm());
        }
    }
}

After that, you need to write some javascript code to add the text field when necessary. See this example :

<script>
    var form = $('#form');
    var formUrl = '{{ url('app_front_form') }}';

    function replaceIfExistsOrRemove(selector, beforeSelectors, dom) {
        var $new = $(dom).find(selector).parent('.form-group');
        if ($new.length == 0) {
            $(selector).parent('.form-group').remove();
        } else {
            if ($(selector).length > 0) {
                $(selector).parent('.form-group').replaceWith($new);
            } else {
                for (i in beforeSelectors) {
                    if ($(beforeSelectors[i]).length > 0) {
                        $(beforeSelectors[i]).parent('.form-group').after($new);
                        return;
                    }
                }
            }
        }
    }

    form.on('change', '#form_city', function() {
        $('#form_submit_button').prop('disabled', true);
        $.get(formUrl, $form.serialize(), function(html) {
            $('#form_submit_button').prop('disabled', false);
            replaceIfExistsOrRemove('#form_cityName', ['#form_city'], html);
        });
    });
</script>

After the form is submitted, if the form is valid, you save this new city and set it in your entity.

I hope this will help you :)

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you for your help @AlphonseD. :) (I liked your comment on stack) Sorry to answer you just now but I didn't see it before. I used the folder service in order to make it work, without javascript, But your way seems to work, I need to check this out. Thanks !

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.