phpformssymfonyabstract-type

load cities in choiceType after select one gouvernorat in a second ChoiceType


I have two entities with association Gouvernorat -> Villes.

This is the Gouvernorat entity:

class Gouvernorat
    {
    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer", nullable=false)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(name="name", type="string", length=100, nullable=false)
     */
    private $name;
    /**
     * @ORM\OneToMany(targetEntity="EntiteBundle\Entity\Ville", mappedBy="idGouvernorat")
     */
    private $villes;
    //getters & setters & constructor
}

and this is the City Entity:

class Ville
{
/**
 * @var int
 *
 * @ORM\Column(name="id", type="integer", nullable=false)
 * @ORM\Id
 * @ORM\GeneratedValue(strategy="IDENTITY")
 */
private $id;

/**
 * @var string
 *
 * @ORM\Column(name="name", type="string", length=200, nullable=false)
 */
private $name;

/**
 * @var \Gouvernorat
 *
 * @ORM\ManyToOne(targetEntity="EntiteBundle\Entity\Gouvernorat",inversedBy="villes")
 * @ORM\JoinColumns({
 *   @ORM\JoinColumn(name="id_gouvernorat", referencedColumnName="id")
 * })
 */
private $idGouvernorat;
}

In a third entity called Etablissement, i have string gouvernorat and string string ville.So i am trying to create an EtablissementType. This form contains two Entity type for the child gouvernorat and ville Brief copy :

class EtablissementType extends AbstractType
{  

 public function buildForm(FormBuilderInterface $builder, array $options)
    {
    $builder
        ->add('nom')           
        ->add('gouvernorat', EntityType::class, array(
            'class' => 'EntiteBundle\Entity\Gouvernorat',
            'choice_label' => 'name',
            'multiple' => false
        ))          
        ->add('ville',EntityType::class,
            array(
                'class' => 'EntiteBundle\Entity\Ville',
                'choice_label' => 'name',
                'multiple' => false
        ))            
        ->add('Enregistrer',SubmitType::class)
        -> setMethod('POST');       
}

when i select gouvernorat i want that all cities of this gouvernorat are loaded i the second ChoiceType so i added this event:

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
            //$builder-> ...

            $builder->addEventListener(
            FormEvents::PRE_SET_DATA,
            function (FormEvent $event) {
                $form = $event->getForm();

                $data = $event->getData();                    
                $gouv = $data-> getGouvernorat();
                $villes = null === $gouv ? array() : $gouv->getVilles();
                $names = array_map(function ($value) {
                    return  $value['name'];
                }, $villes);

                $form->add('ville', ChoiceType::class,
                    array(
                    'choices' => $names
                ));
            }
        );
    }

shortcut for the data(in json forma):

[
{
    "id": 1,
    "name": "ARIANA",
    "villes": [
        {
            "id": 1,
            "name": "RAOUED",
            "idGouvernorat": null
        },
        {
            "id": 2,
            "name": "SIDI THABET",
            "idGouvernorat": null
        },
        {
            "id": 3,
            "name": "ARIANA VILLE",
            "idGouvernorat": null
        },
        {
            "id": 4,
            "name": "LA SOUKRA",
            "idGouvernorat": null
        },
        {
            "id": 5,
            "name": "KALAAT LANDLOUS",
            "idGouvernorat": null
        },
        {
            "id": 6,
            "name": "ETTADHAMEN",
            "idGouvernorat": null
        },
        {
            "id": 7,
            "name": "MNIHLA",
            "idGouvernorat": null
        }
    ]
},
{
    "id": 2,
    "name": "BEJA",
    "villes": [
        {
            "id": 8,
            "name": "BEJA NORD",
            "idGouvernorat": null
        },
        {
            "id": 9,
            "name": "NEFZA",
            "idGouvernorat": null
        },
        {
            "id": 10,
            "name": "GOUBELLAT",
            "idGouvernorat": null
        },
        {
            "id": 11,
            "name": "MEJEZ EL BAB",
            "idGouvernorat": null
        },
        {
            "id": 12,
            "name": "AMDOUN",
            "idGouvernorat": null
        },
        {
            "id": 13,
            "name": "TEBOURSOUK",
            "idGouvernorat": null
        },
        {
            "id": 14,
            "name": "TESTOUR",
            "idGouvernorat": null
        },
        {
            "id": 15,
            "name": "THIBAR",
            "idGouvernorat": null
        },
        {
            "id": 16,
            "name": "BEJA SUD",
            "idGouvernorat": null
        }
    ]
},

etc... The problem, is that the second choiceType always empty that mean $gouv->getVilles(); doesn't work Where is the problem, i didn't find the issue exactly , i usedsymfony documentation--Dynamic Generation for Submitted Forms

<script>
                    var $gouvernorat = $('#etablissement_gouvernorat');
                    // When gouvernorat gets selected ...
                    $gouvernorat.change(function() {
                        // ... retrieve the corresponding form.
                        var $form = $(this).closest('form');
                        // Simulate form data, but only include the selected gouvernorat value.
                        var data = {};
                        data[$gouvernorat.attr('name')] = $gouvernorat.val();
                        // Submit data via AJAX to the form's action path.
                        $.ajax({
                            url : $form.attr('action'),
                            type: $form.attr('method'),
                            data : data,
                            success: function(html) {
                                // Replace current ville field ...
                                $('#etablissement_ville').replaceWith(
                                    // ... with the returned one from the AJAX response.
                                    $(html).find('#etablissement_ville')
                                );
                                // Ville field now displays the appropriate Villes.
                            }
                        });
                    });
                </script>

Solution

  • First when you get your gouv data in the form, I am not sure if it works as expected.

    Replace

    $form = $event->getForm();
    $data = $event->getData();                 
    $gouv = $data->getGouvernorat();
    

    By:

    $form = $event->getForm();
    $data = $event->getData();                
    $gouv = $data->get('gouvernorat')->getData(); // basically, you get the form data for the gouvernorat field
    

    At this moment I think gouvernorat is not populated with its own villes which are stored in the database (I suppose). Now you will need to check if the getGouvernorat method of your $data (which is an Etablissement instance) returns a Gouvernorat instance. If yes you will get this gouvernerat from your database, in order to access all its cities.

    if (!is_null($data->getGouvernorat())) {
        $persistedGouvernorat = $gouvernoratRepository->find($event->getData()->getGouvernorat());;
        if (!is_null($persistedGouvernorat)) {
            $gouverorat = $persistedGouvernorat;
        }
    }
    $villes = $gouvernorat->getVilles();
    

    Then I think you can get rid your issue.