symfony2 chained selectors

后端 未结 3 1872
無奈伤痛
無奈伤痛 2020-11-30 19:48

I have three entities: Country, State and City with the following relationships:

When creating a City, I want two selectors, one for the Country and one for

相关标签:
3条回答
  • 2020-11-30 19:56

    You will need a dedicated FieldType for the chained selectbox. And also an xhr controller that can return child options based on passed parameter. Ofcourse property_path should be set to false.

    0 讨论(0)
  • 2020-11-30 20:08

    OK, I finally figured out how to do it properly:

    namespace Test\TestBundle\Form;
    
    use Symfony\Component\Form\AbstractType;
    use Symfony\Component\Form\FormBuilder;
    
    use Doctrine\ORM\EntityRepository;
    use Symfony\Component\Form\FormEvents;
    use Symfony\Component\Form\Event\DataEvent;
    
    use Test\TestBundle\Entity\Country;
    use Test\TestBundle\Entity\State;
    use Test\TestBundle\Entity\City;
    
    
    class CityType extends AbstractType
    {
        public function buildForm(FormBuilder $builder, array $options)
        {
            $builder->add('name');
    
            $factory = $builder->getFormFactory();
    
            $refreshStates = function ($form, $country) use ($factory) {
                $form->add($factory->createNamed('entity','state', null, array(
                    'class'         => 'Test\TestBundle\Entity\State',
                    'property'      => 'name',
                    'empty_value'   => '-- Select a state --',
                    'query_builder' => function (EntityRepository $repository) use ($country) {
                        $qb = $repository->createQueryBuilder('state')
                            ->innerJoin('state.country', 'country');
    
                        if ($country instanceof Country) {
                            $qb->where('state.country = :country')
                                ->setParameter('country', $country);
                        } elseif (is_numeric($country)) {
                            $qb->where('country.id = :country')
                                ->setParameter('country', $country);
                        } else {
                            $qb->where('country.name = :country')
                                ->setParameter('country', null);
                        }
    
                        return $qb;
                   })
               ));
            };
    
            $setCountry = function ($form, $country) use ($factory) {
                $form->add($factory->createNamed('entity', 'country', null, array(
                    'class'         => 'TestBundle:Country', 
                    'property'      => 'name', 
                    'property_path' => false,
                    'empty_value'   => '-- Select a country --',
                    'data'          => $country,
                )));
            };
    
            $builder->addEventListener(FormEvents::PRE_SET_DATA, function (DataEvent $event) use ($refreshStates, $setCountry) {
                $form = $event->getForm();
                $data = $event->getData();
    
                if ($data == null) {
                    return;
                }
    
                if ($data instanceof City) {
                    $country = ($data->getId()) ? $data->getState()->getCountry() : null ;
                    $refreshStates($form, $country);
                    $setCountry($form, $country);
                }
            });
    
            $builder->addEventListener(FormEvents::PRE_BIND, function (DataEvent $event) use ($refreshStates) {
                $form = $event->getForm();
                $data = $event->getData();
    
                if(array_key_exists('country', $data)) {
                    $refreshStates($form, $data['country']);
                }
            });
        }
    
        public function getName()
        {
            return 'city';
        }
    
        public function getDefaultOptions(array $options)
        {
            return array('data_class' => 'Test\TestBundle\Entity\City');
        }
    }
    

    The jQuery AJAX selector

    $(document).ready(function () {
        $('#city_country').change(function(){
            $('#city_state option:gt(0)').remove();
            if($(this).val()){
                $.ajax({
                    type: "GET",
                    data: "country_id=" + $(this).val(),
                    url: Routing.generate('state_list'),
                    success: function(data){
                        $('#city_state').append(data);
                    }
                });
            }
        });
    });
    

    I hope this will be helpful to somebody else facing the same situation.

    0 讨论(0)
  • 2020-11-30 20:10

    Since your link to this approach is down i decided to complement your excelent answer so anyone can use it:

    In order to execute the following javascript command:

    url: Routing.generate('state_list'),
    

    You need to install FOSJsRoutingBundle that can be found in here.

    ATENTION: in the read me section of the bundle there are instalation instructions but there is something missing. If you use the deps with this:

    [FOSJsRoutingBundle]
    git=git://github.com/FriendsOfSymfony/FOSJsRoutingBundle.git
    target=/bundles/FOS/JsRoutingBundle
    

    You must run the php bin/vendors update before the next steps.

    I'm still trying to find out what route is needed in the routing.yml for this solution to work. As soon as i discover i will edit this answer.

    0 讨论(0)
提交回复
热议问题