如何在另一个选择框中使用相关的选择框?

时间:2012-01-29 11:44:50

标签: forms validation symfony doctrine-orm

如何在Symfony中使用相关的选择框?

让我们说,我有一个包含公司的选择列表,另一个包含所选公司的员工。我如何在Symfony中定义它们? 我已经创建了所有与Javascript相关的代码,但在提交表单并在某些字段上出错时,所有" sub"选择字段重置为null。

任何想法?
谢谢,

编辑:由于这个问题似乎被误解了,我会增加一些准确性:

描述:

  1. 我有一个实体公司,其中包含使用@OneToMany关系的员工列表。
  2. 当我在选择/下拉列表中选择公司时,包含员工的第二个下拉列表将通过jQuery更新。 该部分已完成,效果很好
  3. 提交表单时没有错误,实体表单解决方案正常工作。
  4. 提交包含错误的表单时,第二个下拉列表包含所有可能的值。它们不会在选定的公司上过滤。
  5. 尝试过的解决方案:

    • 我的第一个想法是使用表单实体类型,认为该组件可能以某种方式绑定在另一个字段上。即。根据所选公司的价值更新员工列表。

    不工作,没有办法开箱即用。即使是没有开箱即用的解决方案......

    • 然后我考虑将手动传递所选公司作为参数添加到第二个下拉列表的查询构建器中。

    但是在创建表单时,值为空。这些值仅在 bindRequest

    上设置
    • 考虑使用选择类型。通过Javascript将所有过滤器功能委托给UI。页面加载时的含义,将显示一个空列表,并由Javascript根据所选公司填充。

    这实际上有效,但我认为除了真正难看的编程之外别无他法。

    PS:

    此处已在Symfony2邮件列表,Twitter和官方Symfony 2论坛上提出问题。在发布我的问题之前,我当然已经多次搜索过这些内容。

3 个答案:

答案 0 :(得分:13)

关于你已经尝试过的内容,我认为你应该重试你的第一个/第二个想法:

  

我的第一个想法是使用表单实体类型,思考组件   可以在另一个领域以某种方式绑定。即。更新员工名单   根据所选公司的价值。

您可以使用entity类型填充员工选择框。 您所要做的就是定义好的选择:

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);
    }
}
  

然后我考虑手动将所选公司作为   第二个下拉列表的查询构建器的参数。

此处的示例根据companyId表单的选项过滤员工列表。 您可以通过直接过滤表单数据中的公司来修改此行为。

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)
                ;
            },
        ))
    ;
}

您仍需要在getEmployee()课程中实施setEmployee()Entity\Foo方法。

  

但是在创建表单时,值为空。仅设置值   bindRequest。

没有。使用表单工厂(第三个参数)创建表单时设置值, 或者当你致电$form->setData($foo);时。当您bind表单的新输入时,数据会被修改。

这种方法可能存在问题:绑定到表单的员工ID可能在表单选项列表中不可用,因为您更改了公司(以及员工)。

答案 1 :(得分:5)

我遇到了同样的问题。您必须使用表单事件。我的国家,地区,城市关系的代码示例。

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']);
                    }                    
                });
    }

}

答案 2 :(得分:1)

您可以创建操作,即与公司员工一起返回json数组。 当公司列表发生变化时,您必须通过ajax请求此操作并创建选择的员工列表。

表单控制器操作:

/**
 * @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,公司在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();
}

表单模板:

<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>

有关详细示例,请更详细地描述您的问题。