Symfony2双嵌套动态表单字段 [英] Symfony2 double nested dynamic form fields

查看:64
本文介绍了Symfony2双嵌套动态表单字段的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个带有两个分层动态字段的Symfony2 Form.第一层毫无问题地使用表单事件来实现记录的方式: http://symfony.com/doc/current/cookbook/form/dynamic_form_modification.html#dynamic-generation-for-submitted表单

I have a Symfony2 Form with two layered dynamic fields. The first Layer is no problem implementing the documented way with form events: http://symfony.com/doc/current/cookbook/form/dynamic_form_modification.html#dynamic-generation-for-submitted-forms

但是接着出现第三个字段,该字段取决于第二个字段,该字段已经是动态字段.

But then comes a third field, which depends on the second field, which is already a dynamic field.

为演示该问题,这是我的剥离代码:

To demonstrate the problem, here is my stripped code:

<?php
class ServiceeventType extends AbstractType
{
    /**
     * @param FormBuilderInterface $builder
     * @param array $options
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('park', 'entity', array(
                'class' => 'AppBundle:Park',
                'property' => 'identifyingName',
                'label' => 'Park',
                'required' => true,
                'invalid_message' => 'Choose a Park',
                'placeholder' => 'Please choose',
            ))
            // just a placeholder for the $builder->get('facility')->addEventListener to have something to bind to
            // I'm aware, that this is just a symptom of my problem
            ->add('facility', 'choice', array(
                'choices' => array(),
                'expanded' => true,
                'multiple' => false,
                'label' => 'Facility',
                'required' => false,
                'invalid_message' => 'Choose a Park first',
                'placeholder' => 'Please choose a Park first',
            ))
            // other fields
        ;

        $formModifierPark = function (FormInterface $form, Park $park = null) {
            // overwrite the facility field with the desired entity type
            $form->add('facility', 'entity', array(
                'class' => 'AppBundle:Facility',
                'property' => 'identifyingName',
                'choices' => null === $park ? array() : $park->getFacilities(),
                'label' => 'Facility',
                'required' => true,
                'invalid_message' => 'Choose a Facility',
                'placeholder' => null === $park ? 'Please choose a Park first' : 'Please choose',
            ));
        };

        $formModifierFacility = function (FormInterface $form, Facility $facility = null) {
            $form->add('facilityStatuscode', 'entity', array(
                'class' => 'AppBundle:FacilityStatuscode',
                'property' => 'identifyingName',
                'choices' => null === $facility ? array() : $facility->getFacilityStatuscodeType()->getFacilityStatuscodes(),
                'label' => 'Statuscode',
                'required' => null === $facility ? false : true,
                'invalid_message' => 'Choose a Statuscode',
                'placeholder' => null === $facility ? 'Please choose a Facility first' : 'Please choose',
            ));
        };

        $builder->addEventListener(
            FormEvents::PRE_SET_DATA,
            function (FormEvent $event) use ($formModifierPark) {
                $formModifierPark($event->getForm(), $event->getData()->getPark());
            }
        );
        $builder->get('park')->addEventListener(
            FormEvents::POST_SUBMIT,
            function (FormEvent $event) use ($formModifierPark) {
                $formModifierPark($event->getForm()->getParent(), $event->getForm()->getData());
            }
        );

        $builder->addEventListener(
            FormEvents::PRE_SET_DATA,
            function (FormEvent $event) use ($formModifierFacility) {
                $formModifierFacility($event->getForm(), $event->getData()->getFacility());
            }
        );
        $builder->get('facility')->addEventListener(
            FormEvents::POST_SUBMIT,
            function (FormEvent $event) use ($formModifierFacility) {
                $formModifierFacility($event->getForm()->getParent(), $event->getForm()->getData());
            }
        );
    }

    // more code
}

现在的问题是

设置为$builder->get('facility')->addEventListener(FormEvents::POST_SUBMIT,…的事件侦听器此刻丢失,设施字段被其他事件侦听器覆盖.

The event-listener set with $builder->get('facility')->addEventListener(FormEvents::POST_SUBMIT,… gets lost at the moment, the facility-field is overwritten by the other event-listener.

我尝试了几种解决方法,但事实证明,一旦构建器准备就绪(即,当添加到事件侦听器中时),就不能覆盖表单字段选项,并且以后添加的表单字段不接受新的事件侦听器.

I tried several workarounds, but it turns out, that form field options cannot be overridden and form later added fields don't accept new event listeners, once the builder is ready (i.e. when added inside an event listener).

我真的必须解决这个问题.我想念什么吗? Symfony2的表单引擎是否不能处理两层动态表单字段依赖项?

I really have to solve this. Am I missing something? Is Symfony2's Form engine not able to handle two layered dynamic form field dependencies?

有什么建议吗?

推荐答案

感谢dmnptr的第一条评论(

Thanks to dmnptr's link from his first comment (http://showmethecode.es/php/symfony/symfony2-4-dependent-forms/), I could solve the problem for my case. The trick is, to bind the events to the whole form and not to certain fields (and to PRE_SUBMIT instead of POST_SUBMIT). So my form class now looks like this:

<?php
class ServiceeventType extends AbstractType
{
    /**
     * @param FormBuilderInterface $builder
     * @param array $options
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('park', 'entity', array(
                'class' => 'AppBundle:Park',
                'property' => 'identifyingName',
                'label' => 'Park',
                'required' => true,
                'invalid_message' => 'Choose a Park',
                'placeholder' => 'Please choose',
            ))
            // other fields
        ;

        $addFacilityForm = function (FormInterface $form, $park_id) {
            // it would be easier to use a Park entity here,
            // but it's not trivial to get it in the PRE_SUBMIT events
            $form->add('facility', 'entity', array(
                'class' => 'AppBundle:Facility',
                'property' => 'identifyingName',
                'label' => 'Facility',
                'required' => true,
                'invalid_message' => 'Choose a Facility',
                'placeholder' => null === $park_id ? 'Please choose a Park first' : 'Please Choose',
                'query_builder' => function (FacilityRepository $repository) use ($park_id) {
                    // this does the trick to get the right options
                    return $repository->createQueryBuilder('f')
                        ->innerJoin('f.park', 'p')
                        ->where('p.id = :park')
                        ->setParameter('park', $park_id)
                    ;
                }
            ));
        };
        $builder->addEventListener(
            FormEvents::PRE_SET_DATA,
            function (FormEvent $event) use ($addFacilityForm) {
                $park = $event->getData()->getPark();
                $park_id = $park ? $park->getId() : null;
                $addFacilityForm($event->getForm(), $park_id);
            }
        );
        $builder->addEventListener(
            FormEvents::PRE_SUBMIT,
            function (FormEvent $event) use ($addFacilityForm) {
                $data = $event->getData();
                $park_id = array_key_exists('park', $data) ? $data['park'] : null;
                $addFacilityForm($event->getForm(), $park_id);
            }
        );

        $addFacilityStatuscodeForm = function (FormInterface $form, $facility_id) {
            $form->add('facilityStatuscode', 'entity', array(
                'class' => 'AppBundle:FacilityStatuscode',
                'property' => 'identifyingName',
                'label' => 'Statuscode',
                'required' => true,
                'invalid_message' => 'Choose a Statuscode',
                'placeholder' => null === $facility_id ? 'Please choose a Facility first' : 'Please Chosse',
                'query_builder' => function (FacilityStatuscodeRepository $repository) use ($facility_id) {
                    // a bit more complicated, that's how this model works
                    return $repository->createQueryBuilder('fs')
                        ->innerJoin('fs.facilityStatuscodeType', 'fst')
                        ->innerJoin('AppBundle:Facility', 'f', 'WITH', 'f.facilityStatuscodeType = fst.id')
                        ->where('f.id = :facility_id')
                        ->setParameter('facility_id', $facility_id)
                    ;
                }
            ));
        };
        $builder->addEventListener(
            FormEvents::PRE_SET_DATA,
            function (FormEvent $event) use ($addFacilityStatuscodeForm) {
                $facility = $event->getData()->getFacility();
                $facility_id = $facility ? $facility->getId() : null;
                $addFacilityStatuscodeForm($event->getForm(), $facility_id);
            }
        );
        $builder->addEventListener(
            FormEvents::PRE_SUBMIT,
            function (FormEvent $event) use ($addFacilityStatuscodeForm) {
                $data = $event->getData();
                $facility_id = array_key_exists('facility', $data) ? $data['facility'] : null;
                $addFacilityStatuscodeForm($event->getForm(), $facility_id);
            }
        );


    }

    // more code
}

然后,AJAX的工作方式就像上面文章链接中所建议的一样

The AJAX-stuff then works like suggested in the article link above

这篇关于Symfony2双嵌套动态表单字段的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆