Symfony3 Dynamically Modify Forms with events
Asked Answered
L

2

8

Problem is that nothing is loaded in the municipality field, it goes undefined. In the AJAX code I get the value of the province well. But in the class addMunicipioField.php does not take the value of the $province, it is always nul

enter image description here

I am trying to make a registration form where part of the usual fields (name, nick, password, ...) I also add two dependent fields Municipality and Province.

The codec Controler:

class UserController extends Controller {

private $session;

public function __construct() {
    $this->session = new Session();
}

public function registerAction(Request $request) {

    if (is_object($this->getUser())) {
        return $this->redirect('home');
    }

    $user = new DbUsuario();

    $form = $this->createForm(RegistreUserType::class, $user);

    $form->handleRequest($request);
    if ($form->isSubmitted()) {
        if ($form->isValid()) {
            $em = $this->getDoctrine()->getManager();
            $query = $em->createQuery('SELECT u FROM BackendBundle:DbUsuario u WHERE u.email = :email OR u.nick = :nick')
                    ->setParameter('email', $form->get("email")->getData())
                    ->setParameter('nick', $form->get("nick")->getData());

            $user_isset = $query->getResult();

            if (count($user_isset) == 0) {
                $factory = $this->get("security.encoder_factory");
                $encoder = $factory->getEncoder($user);

                $password = $encoder->encodePassword($form->get("password")->getData(), $user->getSalt());

                $user->setPassword($password);
                $user->setRole("ROLE_USER");
                $user->setImagen(null);

                $em->persist($user);
                $flush = $em->flush();

                if ($flush == null) {
                    $status = "Te has registrado correctamente";
                    $this->session->getFlashBag()->add("status", $status);
                    return $this->redirect("login");
                } else {
                    $status = "No te has registrado correctamente";
                }
            } else {
                $status = "Usuario ya esta registrado.";
            }
        } else {
            $status = "No te has registrado correctamente.";
        }
        $this->session->getFlashBag()->add("status", $status);
    }
    return $this->render('AppBundle:User:register.html.twig', array(
                "form" => $form->createView() # Genera el html del formulario.
    ));
}

The Entity that creates the form is DbUsuario, which has the idMunicipio field.

/** @var \BackendBundle\Entity\DbMunicipios */
private $idMunicipio;

/**
 * Set idMunicipio
 * @param \BackendBundle\Entity\DbMunicipio $idMunicipio
 * @return DbUsuario
 */
public function setIdMunicipio(\BackendBundle\Entity\DbMunicipio $idMunicipio = null) {
    $this->idMunicipio = $idMunicipio;
    return $this;
}

/**
 * Get idMunicipio
 * @return \BackendBundle\Entity\DbMunicipio
 */
public function getIdMunicipio() {
    return $this->idMunicipio;
}

Then the Entity Of DbMunicipio that connects with 'province' with :

/** @var \BackendBundle\Entity\DbProvincia */
private $provincia;

/**@param \BackendBundle\Entity\DbProvincia $provincia
 * @return DbMunicipio
 */
public function setProvincia(\BackendBundle\Entity\DbProvincia $provincia = null){
    $this->provincia = $provincia;
    return $this;
}

// And implement this function.
public function __toString(){
    return $this->getMunicipio();
}

/**@return \BackendBundle\Entity\DbProvincia */
public function getProvincia(){
    return $this->provincia;
}

And the Entity DbProvincia that only has the fields (id (integer), slug (String) and province (String)).

I define the form as follows:

namespace AppBundle\Form;
use ....

class RegistreUserType extends AbstractType {

public function buildForm(FormBuilderInterface $builder, array $options) {
     $factory = $builder->getFormFactory(); 

    $builder->add('nombre', TextType::class, array('label' => 'Nombre',
        'required' => 'required',
        'attr' => array('class' => 'form-nombre form-control')
    ));
    $builder->add('apellido', TextType::class, array('label' => 'Apellido',
        'required' => 'required',
        'attr' => array('class' => 'form-apellido form-control')
    ));
    $builder->add('nick', TextType::class, array('label' => 'Nick',
        'required' => 'required',
        'attr' => array('class' => 'form-nick form-control nick-input')
    ));

    $provinSubscriber = new AddProvinciaField($factory);
    $builder->addEventSubscriber($provinSubscriber);

    $muniSubscriber = new AddMunicipioField($factory);
    $builder->addEventSubscriber($muniSubscriber);

    $builder->add('email', EmailType::class, array('label' => 'Correo electrónico',
        'required' => 'required',
        'attr' => array('class' => 'form-email form-control')
    ));
    $builder->add('password', PasswordType::class, array('label' => 'Password',
        'required' => 'required',
        'attr' => array('class' => 'form-password form-control')
    ));

    $builder->add('Registrarse', SubmitType::class, array("attr" => array("class" => "form-submit btn btn-success")));

}

public function configureOptions(OptionsResolver $resolver) {
    $resolver->setDefaults(array(
        'data_class' => 'BackendBundle\Entity\DbUsuario'
    ));
}

public function getBlockPrefix() { return 'backendbundle_dbusuario'; }
}

I define within the AppBundle \ Form \ eventListener \ AddProvinciaField the classes called in the form:

namespace AppBundle\Form\EventListener;

use ....
use BackendBundle\Entity\DbProvincia;

class AddProvinciaField implements EventSubscriberInterface {
     private $factory;

    public function __construct(FormFactoryInterface $factory) {
        $this->factory = $factory;
    }

    public static function getSubscribedEvents() {
        return array(
            FormEvents::PRE_SET_DATA => 'preSetData',
            FormEvents::PRE_SUBMIT     => 'preSubmit'
        );
    }

    private function addProvinciaForm($form, $provincia) {

       $form -> add('provincia', EntityType::class, array(
            'class'         => 'BackendBundle:DbProvincia',
            'label'         => 'Provincia',
            'placeholder'   => '_ Elegir _',
            'auto_initialize' => false,
            'mapped'        => false,
            'attr'=> array('class' => 'form-provincia form-control provincia-input'),
            'query_builder' => function (EntityRepository $repository) {
                $qb = $repository->createQueryBuilder('provincia');
                return $qb;
            }
        ));
    }

    public function preSetData(FormEvent $event){
        $data = $event->getData();
        $form = $event->getForm();

        if (null === $data) {return;}

        $provincia = ($data->getIdMunicipio()) ? $data->getIdMunicipio()->getProvincia() : null ;
        $this->addProvinciaForm($form, $provincia);
    }

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

        if (null === $data) { return;}

        $provincia = array_key_exists('provincia-input', $data) ? $data['provincia-input'] : null;
        $this->addProvinciaForm($form, $provincia);
    }  
}

And later I define AddMunicipioField.php:

namespace AppBundle\Form\EventListener;

 use ....
 use BackendBundle\Entity\DbProvincia;


 class AddMunicipioField implements EventSubscriberInterface {
    private $factory;

    public function _construct(FormFactoryInterface $factory) {
        $this->factory = $factory;
    }

    public static function getSubscribedEvents() {
        return array(
            FormEvents::PRE_SET_DATA => 'preSetData',
            FormEvents::PRE_SUBMIT     => 'preSubmit'
        );
    }

    private function addMunicipioForm($form, $provincia) {
        $form->add('idMunicipio', EntityType::class, array(
            'class'         => 'BackendBundle:DbMunicipio',
            'label'         => 'Municipio',
            'placeholder'   => '_ Elegir _',
            'auto_initialize' => false,
            'attr'=> array('class' => 'form-municipio form-control municipio-input'),
            'query_builder' => function (EntityRepository $repository) use ($provincia) {
            $qb = $repository->createQueryBuilder('idMunicipio')
                ->innerJoin('idMunicipio.provincia', 'provincia');
            if ($provincia instanceof DbProvincia) {
                $qb->where('idMunicipio.provincia = :provincia')
                ->setParameter('provincia', $provincia);
            
            } elseif (is_numeric($provincia)) {
                $qb->where('provincia.id = :provincia')
                ->setParameter('provincia', $provincia);
          
            } else {
                $qb->where('provincia.provincia = :provincia')
                ->setParameter('provincia', null);
          
            }
            return $qb;
        }
    ));
}

public function preSetData(FormEvent $event){
    $data = $event->getData();
    $form = $event->getForm();

    if (null === $data) { return; }

    $provincia = ($data->getIdMunicipio()) ? $data->getIdMunicipio()->getProvincia() : null ;
    $this->addMunicipioForm($form, $provincia);
}

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

    if (null === $data) { return; }

    $provincia = array_key_exists('provincia_input', $data) ? $data['provincia_input'] : null;
    $this->addMunicipioForm($form, $provincia);
  }
 }

And finally the AJAX request:

$(document).ready(function(){
    var $form = $(this).closest('form');
    $(".provincia-input").change(function(){
        var data = { idMunicipio: $(this).val() };
        $.ajax({
            type: 'POST',
            url: $form.attr('action'),
            data: data,
            success: function(data) {
                for (var i=0, total = data.length; i < total; i++) {
                    $('.municipio-input').append('<option value="' + data[i].id + '">' + data[i].municipio + '</option>');
                }
            }
        });
    });
});

I added var_dump and alert() in the code. This is the way output.

In this case, the value of province is always null.

 addMunicipioField.php
 public function preSetData(FormEvent $event){
    $data = $event->getData();
    $form = $event->getForm();

    if (null === $data) {
        return;
    }

    $provincia = ($data->getIdMunicipio()) ? $data->getIdMunicipio()->getProvincia() : null ;
    var_dump('presetdata');
    var_dump($provincia);
    $this->addMunicipioForm($form, $provincia);
}

AJAX:

$(document).ready(function(){
    var $form = $(this).closest('form');
    $(".provincia-input").change(function(){
        alert($('.provincia-input').val()); // THIS IS CORRECT VALUE, INTEGER.
        var data = { idMunicipio: $(this).val() };
        $.ajax({
            type: 'POST',
            url: $form.attr('action'),
            data: data,
            success: function(data) {
                alert(data);
                alert(data.length); // THIS IS INCORRECT.
                for (var i=0, total = data.length; i < total; i++) {
                    $('.municipio-input').append('<option value="' + data[i].id + '">' + data[i].municipio + '</option>');
                }
            }
        });
    });
});

enter image description here

Another point of view The entities are the same. In this case it works but I must press the send button. How could I do it without pressing the button, that it was automatic change?

The class RegistreUserType extends AbstractType I add the following lines.

$builder -> add('provincia', EntityType::class, array(
        'class'         => 'BackendBundle:DbProvincia',
        'label'         => 'Provincia',
        'placeholder'   => '_ Elegir _',
        'auto_initialize' => false,
        'mapped'        => false,
        'attr'=> array('class' => 'form-provincia form-control provincia-input'),
        'query_builder' => function (EntityRepository $repository) {
            $qb = $repository->createQueryBuilder('provincia');
            return $qb;
        }
    ));
 
    $builder->add('idMunicipio', EntityType::class, array(
        'class' => 'BackendBundle:DbMunicipio',
        'label'         => 'Municipio',
        'placeholder'   => '_ Elegir _',
        'auto_initialize' => false,
        'mapped'        => false,
        'attr'=> array('class' => 'form-municipio form-control municipio-input')
    ));
    
    $builder->addEventSubscriber(new AddMunicipioField());

The new class AddMunicpioField():

class AddMunicipioField implements EventSubscriberInterface {

public static function getSubscribedEvents() {
    return array(
        FormEvents::PRE_SUBMIT => 'preSubmit',
        FormEvents::PRE_SET_DATA => 'preSetData',
    );
}

public function preSubmit(FormEvent $event){
    $data = $event->getData();
    $this->addField($event->getForm(), $data['provincia']);
}

protected function addField(Form $form, $provincia){
    $form->add('idMunicipio', EntityType::class, array(
        'class'         => 'BackendBundle:DbMunicipio',
        'label'         => 'Municipio',
        'placeholder'   => '_ Elegir _',
        'auto_initialize' => false,
        'mapped'        => false,
        'attr'=> array('class' => 'form-municipio form-control municipio-input'),
        'query_builder' => function(EntityRepository $er) use ($provincia){
            $qb = $er->createQueryBuilder('idMunicipio')
                    ->where('idMunicipio.provincia = :provincia')
                    ->setParameter('provincia', $provincia);
            
            return $qb;
        }
    ));
 }

Codec Ajax:

$(document).ready(function () {
$('.provincia-input').change(function () {       
    var $form = $(this).closest('form');
    var data = $('.provincia-input').serialize();
    $.ajax({
        url: $form.attr('action'),
        type: 'POST',
        data: data,
        success: function (data) {
            $('.municipio-input').replaceWith($(html).find('.municipio-input'));
            }
        });
    });
});
Lusatia answered 12/3, 2017 at 20:37 Comment(11)
Are you sure the id of your input is #provincia-input? shouldn't it be .provincia-input as it is a classMandell
If you are absolutely right. Now the ajax part reacts. But it still shows no results. I think it's from the query but I do not know why.Lusatia
Now I get the error: The CSRF token is invalid. Please try to resubmit the form.Lusatia
php.ini change value of Maximum allowed size for uploaded files. upload_max_filesize = 20048M and ; How many GET/POST/COOKIE input variables may be accepted max_input_vars = 25000 But the error still persists.Lusatia
Solved error CSRF change script AJAX.Lusatia
Can you provide a log of what is returned by the ajax callIlene
Hello guys the bounty for any that solves the problem, this * ... large audience. A detailed canonical answer is required to address ... * was to select something, thank you for your time.Overhear
Okay, thanks, I'll do it now.Lusatia
@VincentT You can, look at my edit, thanks for your interest. I have seen that in the class addMunicipioField does not collect the variable & province correctly .But i do not know why.Lusatia
@Lusatia What kind of computations are you doing in the controller could you provide that code aswel.Ilene
@VicentT First thanks. I added the controller code. Thanks for your interest.Lusatia
L
0

Solved!!

In my form I added the call to the two new classes:

$builder -> addEventSubscriber(new AddMunicipioFieldSubscriber('idMunicipio'));
$builder -> addEventSubscriber(new AddProvinceFieldSubscriber('idMunicipio'));

The firth select is province, this is the class:

class AddProvinceFieldSubscriber implements EventSubscriberInterface {
private $propertyPathToMunicipio;

public function __construct($propertyPathToMunicipio) {
    $this->propertyPathToMunicipio = $propertyPathToMunicipio;
}

public static function getSubscribedEvents() {
    return array(
        FormEvents::PRE_SET_DATA => 'preSetData',
        FormEvents::PRE_SUBMIT   => 'preSubmit'
    );
}

private function addProvinceForm($form, $Province = null) {
    $formOptions = array(
        'class'         => 'BackendBundle:DbProvincia',
        'mapped'        => false,
        'label'         => 'Provincia',
        'attr'          => array(
            'class' => 'class_select_provincia',
        ),
    );

    if ($Province) {
        $formOptions['data'] = $Province;
    }

    $form->add('provincia', EntityType::class, $formOptions);
}

public function preSetData(FormEvent $event){
    $data = $event->getData();
    $form = $event->getForm();

    if (null === $data) {
        return;
    }

    $accessor = PropertyAccess::createPropertyAccessor();

    $municipio    = $accessor->getValue($data, $this->propertyPathToMunicipio);
    $provincia = ($municipio) ? $municipio->getIdMunicipio()->getProvincia() : null;

    $this->addProvinceForm($form, $provincia);
}

public function preSubmit(FormEvent $event){
    $form = $event->getForm();

    $this->addProvinceForm($form);
}
}

The second class is Municipi:

class AddMunicipioFieldSubscriber implements EventSubscriberInterface {
//put your code here

private $propertyPathToMunicipio;

public function __construct($propertyPathToMunicipio){
    $this->propertyPathToMunicipio = $propertyPathToMunicipio;
}

public static function getSubscribedEvents(){
    return array(
        FormEvents::PRE_SET_DATA  => 'preSetData',
        FormEvents::PRE_SUBMIT    => 'preSubmit'
    );
}

private function addCityForm($form, $province_id){
    $formOptions = array(
        'class'         => 'BackendBundle:DbMunicipio',
        'label'         => 'Municipio',
        'attr'          => array(
            'class' => 'class_select_municipio',
        ),
        'query_builder' => function (EntityRepository $repository) use ($province_id) {
            $qb = $repository->createQueryBuilder('municipio')
                ->innerJoin('municipio.provincia', 'provincia')
                ->where('provincia.id = :provincia')
                ->setParameter('provincia', $province_id)
            ;

            return $qb;
        }
    );

    $form->add($this->propertyPathToMunicipio, EntityType::class, $formOptions);
}

public function preSetData(FormEvent $event){
    $data = $event->getData();
    $form = $event->getForm();

    if (null === $data) {
        return;
    }

    $accessor    = PropertyAccess::createPropertyAccessor();

    $municipio        = $accessor->getValue($data, $this->propertyPathToMunicipio);
    $province_id = ($municipio) ? $municipio->getIdMunicipio()->getProvincia()->getId() : null;

    $this->addCityForm($form, $province_id);
}

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

    $province_id = array_key_exists('provincia', $data) ? $data['provincia'] : null;

    $this->addCityForm($form, $province_id);
} 
}

The controled add this function:

    public function municipioTestAction(Request $request){
    $provincia_id = $request->get('provincia_id');

    $em = $this->getDoctrine()->getManager();
    $provincia = $em->getRepository('BackendBundle:DbMunicipio')->findByProvinceId($provincia_id);

    return new JsonResponse($provincia);
}

Where the function findByProvinceId, I create it as a repository of the entity DbMunicipio.

class DbMunicipioRepository extends EntityRepository{

public function findByProvinceId($provincia_id){

    $query = $this->getEntityManager()->createQuery("
        SELECT muni
        FROM BackendBundle:DbMunicipio muni
        LEFT JOIN muni.provincia provin
        WHERE provin.id = :provincia_id
    ")->setParameter('provincia_id', $provincia_id);

    return $query->getArrayResult();
} 
}

And de codec AJAX.

$(document).ready(function () {
$(".class_select_provincia").change(function(){
    var data = {
        provincia_id: $(this).val()
    };

    $.ajax({
        type: 'POST',
        url: URL+'/municipio-test',
        data: data,
        success: function(data) {

            var $muni_selector = $('.class_select_municipio');
            alert(data);
            $muni_selector.html('<option>Ciudad</option>');

            for (var i=0, total = data.length; i < total; i++) {
                $muni_selector.append('<option value="' + data[i].id + '">' + data[i].municipio + '</option>');
            }
        }
    });
});
});
Lusatia answered 24/3, 2017 at 0:19 Comment(0)
L
3

I did not notice a field or a property called 'select_provincia' in neither your entity, nor the main form, so I will try guessing, that it probably should be called simply 'provincia', as that is the name for both the property in municipality entity and in the form subscriber for municipality. Also in AddMunicipioField.php you should change this code:

if ($provincia instanceof DbProvincia) {
   $qb->where('idMunicipio.provincia = :provincia')
      >setParameter('provincia', $provincia);
} 

to this:

if ($provincia instanceof DbProvincia) {
   $qb->where('idMunicipio.provincia = :provincia')
      >setParameter('provincia', $provincia->getId());
} 

since when querying you will be comparing provincia to the ID of province.

Further more, make sure you have implemented the __toString() method in the municipality entity, so that symfony would know how to convert that object to a string in order to show it in the select list.

Hope this helps :)


Seeing that you have added new information i will update my answer:

Firstly, In the AddMunicipioField.php you still have basically the same error: the array key is going to be called the same way you name your field, in this case not 'provincia_input', but 'provincia'. You can see the data that was posted to you by calling "dump($data); die;" just before you check if the array key exists (check for a key name "provincia", as you can see the name matches what you have specified when adding the field to the form (AddProvinciaField.php):

$form -> add('provincia', EntityType::class

Another thing I have noticed in the first js snippet you have posted is that in this part of code:

$(".provincia-input").change(function(){
    var data = { idMunicipio: $(this).val() };
    $.ajax({
        type: 'POST',
        url: $form.attr('action'),
        data: data,
        success: function(data) {
            for (var i=0, total = data.length; i < total; i++) {
                $('.municipio-input').append('<option value="' + data[i].id + '">' + data[i].municipio + '</option>');
            }
        }
    });
});

you are taking the input from $(".provincia-input") and sending it as a value for a field called "idMunicipio", which in you situation I don't think makes any sense.


Lastly, I will discus the errors that were made in the last piece of the JS you've posted:

  $(document).ready(function () {
    $('.provincia-input').change(function () {       
        var $form = $(this).closest('form');
        var data = $('.provincia-input').serialize();
        $.ajax({
            url: $form.attr('action'),
            type: 'POST',
            data: data,
            success: function (data) {
                $('.municipio-input').replaceWith($(html).find('.municipio-input'));
            }
        });
    });
  });

First of all, class names are not supposed to be used for identifying the fields that you are using. By definition they are supposed to be used multiple time in the document and describe only style, which might lead to some unexpected behaviour as your codebase grows. Please assign proper ID values to the inputs that you are going to be querying and especially replacing so that you could identify them correctly.

Secondly, please refer to the JS code posted in the official Symfony tutorial by following this link. As you can see the proper way to post data back to the server is not by sending a lone property like you are trying to do in this line:

var data = $('.provincia-input').serialize();

but rather by sending the property as a part of the forms data. So as in the tutorial I've posted, please first create an empty data object:

var data = {};

then add the province value to it:

data[$(this).attr('name')] = $(this).val();

Thirdly, this part of code is clearly incorrect:

success: function (data) {
        $('.municipio-input').replaceWith($(html).find('.municipio-input'));
}

As you can see the html variable is undefined in that part of code. This of course is because the variable that you are supposed to be using in this case is called data (the response that you have gotten from the server). So please change it to this:

success: function (data) {
        $('.municipio-input').replaceWith($(data).find('.municipio-input'));
}

Lastly, if you are still learning SF and web programming, I would like to suggest taking the bottom up approach to advance your programming knowledge instead, since this case is pretty complex and issues that prevented your code from working still require deeper understanding of the technologies you are using. I would personally suggest reading up on HTML attribute usage, Symfony form handling, read up on what data is available to you during each Symfony form event and maybe try using the dumper component of symfony more to debug your code, since var_dump is really a very inefficient way to debug SF code (would have solved many problems for you).

Laaland answered 15/3, 2017 at 14:30 Comment(2)
Thanks for comment. I have modified the select_province in the code. You were right, I wrote it wrong in the edition. On the function __String () and the tapeworm written in the entity. I added the modification in the SQL query. $ Qb-> where ('town_name.province =: province') > SetParameter ('province', $ province-> getId ()); But still not working.Lusatia
Hello, first thank you. The truth is that if I am learning to use symfony. I will take into account your advice and take a closer look at what you have explained to me and I'm sorry for my bad english Thank you.Lusatia
L
0

Solved!!

In my form I added the call to the two new classes:

$builder -> addEventSubscriber(new AddMunicipioFieldSubscriber('idMunicipio'));
$builder -> addEventSubscriber(new AddProvinceFieldSubscriber('idMunicipio'));

The firth select is province, this is the class:

class AddProvinceFieldSubscriber implements EventSubscriberInterface {
private $propertyPathToMunicipio;

public function __construct($propertyPathToMunicipio) {
    $this->propertyPathToMunicipio = $propertyPathToMunicipio;
}

public static function getSubscribedEvents() {
    return array(
        FormEvents::PRE_SET_DATA => 'preSetData',
        FormEvents::PRE_SUBMIT   => 'preSubmit'
    );
}

private function addProvinceForm($form, $Province = null) {
    $formOptions = array(
        'class'         => 'BackendBundle:DbProvincia',
        'mapped'        => false,
        'label'         => 'Provincia',
        'attr'          => array(
            'class' => 'class_select_provincia',
        ),
    );

    if ($Province) {
        $formOptions['data'] = $Province;
    }

    $form->add('provincia', EntityType::class, $formOptions);
}

public function preSetData(FormEvent $event){
    $data = $event->getData();
    $form = $event->getForm();

    if (null === $data) {
        return;
    }

    $accessor = PropertyAccess::createPropertyAccessor();

    $municipio    = $accessor->getValue($data, $this->propertyPathToMunicipio);
    $provincia = ($municipio) ? $municipio->getIdMunicipio()->getProvincia() : null;

    $this->addProvinceForm($form, $provincia);
}

public function preSubmit(FormEvent $event){
    $form = $event->getForm();

    $this->addProvinceForm($form);
}
}

The second class is Municipi:

class AddMunicipioFieldSubscriber implements EventSubscriberInterface {
//put your code here

private $propertyPathToMunicipio;

public function __construct($propertyPathToMunicipio){
    $this->propertyPathToMunicipio = $propertyPathToMunicipio;
}

public static function getSubscribedEvents(){
    return array(
        FormEvents::PRE_SET_DATA  => 'preSetData',
        FormEvents::PRE_SUBMIT    => 'preSubmit'
    );
}

private function addCityForm($form, $province_id){
    $formOptions = array(
        'class'         => 'BackendBundle:DbMunicipio',
        'label'         => 'Municipio',
        'attr'          => array(
            'class' => 'class_select_municipio',
        ),
        'query_builder' => function (EntityRepository $repository) use ($province_id) {
            $qb = $repository->createQueryBuilder('municipio')
                ->innerJoin('municipio.provincia', 'provincia')
                ->where('provincia.id = :provincia')
                ->setParameter('provincia', $province_id)
            ;

            return $qb;
        }
    );

    $form->add($this->propertyPathToMunicipio, EntityType::class, $formOptions);
}

public function preSetData(FormEvent $event){
    $data = $event->getData();
    $form = $event->getForm();

    if (null === $data) {
        return;
    }

    $accessor    = PropertyAccess::createPropertyAccessor();

    $municipio        = $accessor->getValue($data, $this->propertyPathToMunicipio);
    $province_id = ($municipio) ? $municipio->getIdMunicipio()->getProvincia()->getId() : null;

    $this->addCityForm($form, $province_id);
}

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

    $province_id = array_key_exists('provincia', $data) ? $data['provincia'] : null;

    $this->addCityForm($form, $province_id);
} 
}

The controled add this function:

    public function municipioTestAction(Request $request){
    $provincia_id = $request->get('provincia_id');

    $em = $this->getDoctrine()->getManager();
    $provincia = $em->getRepository('BackendBundle:DbMunicipio')->findByProvinceId($provincia_id);

    return new JsonResponse($provincia);
}

Where the function findByProvinceId, I create it as a repository of the entity DbMunicipio.

class DbMunicipioRepository extends EntityRepository{

public function findByProvinceId($provincia_id){

    $query = $this->getEntityManager()->createQuery("
        SELECT muni
        FROM BackendBundle:DbMunicipio muni
        LEFT JOIN muni.provincia provin
        WHERE provin.id = :provincia_id
    ")->setParameter('provincia_id', $provincia_id);

    return $query->getArrayResult();
} 
}

And de codec AJAX.

$(document).ready(function () {
$(".class_select_provincia").change(function(){
    var data = {
        provincia_id: $(this).val()
    };

    $.ajax({
        type: 'POST',
        url: URL+'/municipio-test',
        data: data,
        success: function(data) {

            var $muni_selector = $('.class_select_municipio');
            alert(data);
            $muni_selector.html('<option>Ciudad</option>');

            for (var i=0, total = data.length; i < total; i++) {
                $muni_selector.append('<option value="' + data[i].id + '">' + data[i].municipio + '</option>');
            }
        }
    });
});
});
Lusatia answered 24/3, 2017 at 0:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.