2

Using Symfony2.3.4 and PHP5.6.3

People, I've been looking for this issue for a while now and yes, I've found some similar ones and even found this in the Cookbook.

Now you'd say, "This guy is pretty slow", bingo, I am. Please help me out because I can't seem to get this or any other example I've encountered to help me in my own problem.

What I need is to populate a select field when the user selects an item from another select field. All this happens in a standard-CRUDgenerated-Symfony2 form. Both selects stand for an entity collection each(Zone and UEB), being Zone the independent one.

Community: Stop talking and give me the code!

Me: OK, here is what I have so far:

//ReferenceController.php

public function newAction() {
    $entity = new Reference();
    $form = $this->createCreateForm($entity);

    return $this->render('CCBundle:Reference:new.html.twig', array(
                'entity' => $entity,
                'form' => $form->createView(),
    ));
}

public function createAction(Request $request) {
    $entity = new Reference();
    $form = $this->createCreateForm($entity);
    $form->bind($request);
    /*
    var_dump($form->get('UEB')->getData());
    var_dump($form->get('UEB')->getNormData());
    var_dump($form->get('UEB')->getViewData());
    die();
    */
    if ($form->isValid()) {
        $em = $this->getDoctrine()->getManager();
        $em->persist($entity);
        $em->flush();

        return $this->redirect($this->generateUrl('reference_show', array('id' => $entity->getId())));
    }

    return $this->render('CCBundle:Reference:new.html.twig', array(
                'entity' => $entity,
                'form' => $form->createView(),
    ));
}


private function createCreateForm(Reference $entity) {
    $form = $this->createForm(new ReferenceType(), $entity, array(
        'action' => $this->generateUrl('reference_create'),
        'method' => 'POST',
    ));

    return $form;
}

And

//ReferenceType.php
public function buildForm(FormBuilderInterface $builder, array $options) {
    $builder
            ->add('suffix')
            ->add('zone', null, array(
                'required' => true,
            ))
    ;

    //What follows is for populating UEB field accordingly, 
    //whether it's a "createForm" or an "editForm"

    if ($options['data']->getId() !== null) {
        $formModifier = function (FormInterface $form, Zone $zone = null) {
            $UEBs = null === $zone ? array() : $zone->getUEBs();
            $form->add('UEB', 'entity', array(
                'required' => true,
                'label' => 'UEB',
                'class' => 'CCBundle:UEB',
                //                'empty_value' => '',
                'choices' => $UEBs,
            ));
        };

        $builder->addEventListener(
                FormEvents::PRE_SET_DATA, function (FormEvent $event) use ($formModifier) {
            $data = $event->getData();
            $formModifier($event->getForm(), $data->getZone());
        });
    } else {
        $formModifier = function (FormInterface $form) {
            $form->add('UEB', 'entity', array(
                'required' => true,
                'label' => 'UEB',
                'class' => 'CCBundle:UEB',
                'query_builder' =>
                function(EntityRepository $er) {
            return $er->createQueryBuilder('u')
                            ->where('u.zone = :zone')
                            ->setParameter('zone', $er->findFirstZone());
        }
                    )
            );
        };

        $builder->addEventListener(
                FormEvents::PRE_SET_DATA, function (FormEvent $event) use ($formModifier) {
            $formModifier($event->getForm());
        });
    }

And

//base.js
    var goalURL = "" + window.location;
        if (goalURL.slice(-13) === 'reference/new' || goalURL.match(/reference\/\d+\/edit$/))
    {

        //case new reference
        goalURL = goalURL.replace('reference/new', 'reference/update_uebs/');
        //case edit reference
        goalURL = goalURL.replace(/reference\/\d+\/edit/, 'reference/update_uebs/');

        //this is the function run every time the "new" or "edit" view is loaded
        //and every time the Zone select field is changed
        var runUpdateUEBs = function() {
            $.getJSON(goalURL, {id: $('#cc_ccbundle_reference_zone').val()}, function(response) {
            $('#cc_ccbundle_reference_UEB').children('option').remove();
            var non_selected_options = [];
            var index = 0;
            $.each(response, function(key, val) {
                var option = $('<option selected="selected"></option>');
                option.text(val);
                option.val(key);
                option.prop('selected', 'selected');
                option.appendTo($('#cc_ccbundle_reference_UEB'));
                non_selected_options[index++] = $(option);
            });

            var amount = non_selected_options.length;
            if (amount > 1)
                $.each(non_selected_options, function(key, val) {
                     if (amount - 1 === key)
                     val.attr('selected', false);
                });
            });
    };
    runUpdateUEBs();

    $('#cc_ccbundle_reference_zone').bind({
        change: runUpdateUEBs
    });
}

And

//ReferenceController.php
//this is where "goalURL" goes
function updateUEBsAction() {
    $id = $this->getRequest()->get('id');
    $em = $this->getDoctrine()->getManager();
    $uebs = $em->getRepository('CCBundle:UEB')->findBy(array('zone' => $id));

    $ids_and_names = array();
    foreach ($uebs as $u) {
        $ids_and_names[$u->getId()] = $u->getName();
    }

    return new \Symfony\Component\HttpFoundation\Response(json_encode($ids_and_names));
}

With this I can load the UEBs corresponding the Zone being shown at the moment and every time a new Zone is selected alright, but only visually, not internally, hence: the select populates fine but when I submit the form it doesn't go through with it and outputs "This value is not valid" on the UEB field and the

var_dump($form->get('UEB')->getData());
var_dump($form->get('UEB')->getNormData());
var_dump($form->get('UEB')->getViewData());
die();

from above outputs

null
null
string <the_value_of_the_option_tag> (length=1)

I need to know how to populate the select AND the internal data to be submitted too.

Thanks for bearing with this simple explanation.

I'm listening(reading).

1 Answer 1

4

Here is the answer I was looking for, it looks a lot like the one in the cookbook but somehow I understood this one better and I was able to apply it to my own problem, it only needed a few tweaks in the ajax call and the corresponding action, but only regarding my own problem.

thanks to all who cared to read my question and special thanks to Joshua Thijssen for his post.

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

1 Comment

Thanks for the credits. As you said, the solution will most like be the same (there aren't really that many different ways of doing these kind of things with forms), but with a little bit of different rephrasing, it can sometimes make the difference.

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.