How to use select box related on another select box?

后端 未结 3 725
清酒与你
清酒与你 2021-01-30 23:16

How to use related select boxes in Symfony ?

Let\'s say, I have a select list containing compagnies and another containing employees of the selected company. How do I d

相关标签:
3条回答
  • 2021-01-30 23:39

    I had the same problem. You must use form events. My code example with country, region, city relations.

    namespace Orfos\UserBundle\Form\Type;
    
    ///import form events namespace
    use Symfony\Component\EventDispatcher\EventSubscriberInterface;
    use Symfony\Component\Form\FormEvents;
    use Symfony\Component\Form\Event\DataEvent;
    
    class RegistrationFormType extends BaseType
    {
    
        private $request;
    
        public function __construct($class, $request, $doctrine)
        {
            parent::__construct($class);
            $this->request = $request;
            $this->doctrine = $doctrine;
        }
    
        public function buildForm(FormBuilder $builder, array $options)
        {
            parent::buildForm($builder, $options);
            //other fields
    
            $locale = $this->request->getLocale();
    
            $builder->add('country', 'entity', array(
                'class' => 'Orfos\CoreBundle\Entity\Country',
                'property' => $locale . 'name',
                'label' => 'register.country.label',
                'query_builder' => function(EntityRepository $er) {
                    return $er->createQueryBuilder('c')
                                    ->select('c', 't')
                                    ->join('c.translations', 't');
                },
            ));
    
            $factory = $builder->getFormFactory();
            $refreshRegion = function ($form, $country) use ($factory, $locale) {
                        $form->add($factory->createNamed('entity', 'region', null, array(
                                    'class' => 'Orfos\CoreBundle\Entity\Region',
                                    'property' => $locale . 'name',
                                    'label' => 'register.region.label',
                                    'query_builder' => function (EntityRepository $repository) use ($country) {
                                        $qb = $repository->createQueryBuilder('region')
                                                ->select('region', 'translation')
                                                ->innerJoin('region.country', 'country')
                                                ->join('region.translations', 'translation');
    
                                        if ($country instanceof Country) {
                                            $qb = $qb->where('region.country = :country')
                                                    ->setParameter('country', $country);
                                        } elseif (is_numeric($country)) {
                                            $qb = $qb->where('country.id = :country_id')
                                                    ->setParameter('country_id', $country);
                                        } else {
                                            $qb = $qb->where('country.id = 1');
                                        }
    
                                        return $qb;
                                    }
                                )));
                    };
            $factory = $builder->getFormFactory();
            $refreshCity = function($form, $region) use ($factory, $locale) {
                        $form->add($factory->createNamed('entity', 'city', null, array(
                                    'class' => 'Orfos\CoreBundle\Entity\City',
                                    'property' => $locale . 'name',
                                    'label' => 'register.city.label',
                                    'query_builder' => function (EntityRepository $repository) use ($region) {
                                        $qb = $repository->createQueryBuilder('city')
                                                ->select('city', 'translation')
                                                ->innerJoin('city.region', 'region')
                                                ->innerJoin('city.translations', 'translation');
    
                                        if ($region instanceof Region) {
                                            $qb = $qb->where('city.region = :region')
                                                    ->setParameter('region', $region);
                                        } elseif (is_numeric($region)) {
                                            $qb = $qb->where('region.id = :region_id')
                                                    ->setParameter('region_id', $region);
                                        } else {
                                            $qb = $qb->where('region.id = 1');
                                        }
    
                                        return $qb;
                                    }
                                )));
                    };
    
            $builder->addEventListener(FormEvents::PRE_SET_DATA, function (DataEvent $event) use ($refreshRegion, $refreshCity) {
                        $form = $event->getForm();
                        $data = $event->getData();
    
                        if ($data == null){
                            $refreshRegion($form, null);
                            $refreshCity($form, null);
                        }
    
                        if ($data instanceof Country) {
                            $refreshRegion($form, $data->getCountry()->getRegions());
                            $refreshCity($form, $data->getRegion()->getCities());
                        }
                    });
    
            $builder->addEventListener(FormEvents::PRE_BIND, function (DataEvent $event) use ($refreshRegion, $refreshCity) {
                        $form = $event->getForm();
                        $data = $event->getData();
    
                        if (array_key_exists('country', $data)) {
                            $refreshRegion($form, $data['country']);
                        }
                        if (array_key_exists('region', $data)) {
                            $refreshCity($form, $data['region']);
                        }                    
                    });
        }
    
    }
    
    0 讨论(0)
  • 2021-01-30 23:39

    You can create action, that will be return json array with employees of company. When the list of companies changed you must request this action by ajax and create select list of employees.

    Form controller action:

    /**
     * @Route("/job", name="_demo_job")
     * @Template()
     */
    public function jobAction()
    {
        $form = $this->createForm(new JobType());
    
        $request = $this->getRequest();
        if ($request->getMethod() == 'POST') {
            $form->bindRequest($request);
            $data = $form->getData();
            // some operations with form data
        }
    
        return array(
            'form' => $form->createView(),
        );
    }
    

    Conroller, that return employees by company in json:

    /**
     * Finds all employees by company
     *
     * @Route("/{id}/show", name="employees_by_category")
     */
    public function listByCompanyAction($id)
    {
        $request = $this->getRequest();
    
        if ($request->isXmlHttpRequest() && $request->getMethod() == 'POST') {
            $em = $this->getDoctrine()->getEntityManager();
    
            $company = $em->getRepository('AcmeDemoBundle:Company')->find($id);
    
            // create array for json response
            $empoloyees = array();
            foreach ($company->getEmployees() as $employee) {
                $empoloyees[] = array($employee->getId(), $employee->getName());
            }
    
            $response = new Response(json_encode($empoloyees));
            $response->headers->set('Content-Type', 'application/json');
    
            return $response;
        }
        return new Response();
    }
    

    Form template:

    <script type="text/javascript">
        $(document).ready(function() {
            $('#form_company').change(function() {
                var companyId = $(this).val();
                $.post('{{ route }}/' + companyId + '/show', function(data) {
                    // last selected employee
                    var selectedVal = $('option:selected', '#form_employee').attr('value');
    
                    $('#form_employee option').remove();
                    for (i in data) {
                        // create option with employee
                        var option = $('<option></option>').
                            attr('value', data[i][0]).
                            text(data[i][1]);
                        // set selected employee
                        if (data[i][0] == selectedVal) {
                            option.attr('selected', 'selected');
                        }
                        // append to employee to employees select
                        $('#form_employee').append(option);
                    }
                }, 'json');
            })
    
            // request employees by company
            $('#form_company').change();
        })
    
    </script>
    
    <form action="{{ path('_demo_job') }}" method="post" {{ form_enctype(form) }}>
        {{ form_widget(form) }}
    
        <input type="submit" />
    </form>
    

    For more detail example you should describe your problem in more detail.

    0 讨论(0)
  • 2021-01-30 23:41

    Concerning what you already tried, I think you should retry your first/second ideas:

    My first idea was to use the form entity type, thinking the component could be binded somehow on another field. ie. update list of employees based on the value of the selected company.

    You can populate the employees select box using the entity type. All you have to do is to define the good options:

    class FooType extends AbstractType
    {
        public function buildForm(FormBuilder $builder, array $options)
        {
            $builder
                ->add('employee', 'entity', array(
                    'class' => 'Entity\Employee',
                    'query_builder' => function ($repository) use($options) {
                        return $repository
                            ->createQueryBuilder('e')
                            ->where('e.company = :company')
                            ->setParameter('company', $options['companyId'])
                        ;
                    },
                ))
            ;
        }
    
        public function getDefaultOptions(array $options)
        {
            return array('data_class' => 'Entity\Foo', 'companyId' => null);
        }
    }
    

    Then I thought about manually passing the selected company as parameter to the query builder of the second dropdown list.

    The example here filters the employees list based on the companyId form's option. You can modify this behavior by filtering directly on the company present in the form's data.

    public function buildForm(FormBuilder $builder, array $options)
    {
        $companyId = $builder->getData()->getCompanyId();
        $builder
            ->add('employee', 'entity', array(
                'class' => 'Entity\Employee',
                'query_builder' => function ($repository) use ($companyId) {
                    return $repository
                        ->createQueryBuilder('e')
                        ->where('e.company = :company')
                        ->setParameter('company', $companyId)
                    ;
                },
            ))
        ;
    }
    

    You still have to implement the getEmployee() and setEmployee() methods in your Entity\Foo class.

    But when the form is created, the values are empty. The values are only set on bindRequest.

    No. Values are set when you create you form using form factory (third argument), OR when you call $form->setData($foo);. Data is modified when you bind new inputs to the form.

    There could be a problem with this approach: it's possible that the employee id bound to the form is not available in the form choice list, because you changed the company (and thus the employees).

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