带有表单事件的提交表单的动态生成 [英] Dynamic Generation for Submitted Forms with Form events

查看:26
本文介绍了带有表单事件的提交表单的动态生成的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我对 FormEvents 有点问题,我想要动态填充 3 个字段.我解释一下,我有3个字段:项目>框>单元格,用户选择一个项目,框列表更新,他选择一个框,单元格列表更新.

I've a little problem with FormEvents, I want do 3 fields populated dynamically. I explain, I've 3 fields: Project > Box > Cell, the user choose a Project, the Box list is updated, he choose a Box, the cell list is updated.

为了做到这一点,我使用 FormEvent 就像文档说的那样 (http://symfony.com/doc/current/cookbook/form/dynamic_form_modification.html#cookbook-form-events-submitted-data)

To do it, I use FormEvent like the documentation say (http://symfony.com/doc/current/cookbook/form/dynamic_form_modification.html#cookbook-form-events-submitted-data)

但是我有一个问题,只有一个字段动态更新,它是有效的,但对于2个字段没有......实际上用户可以选择一个项目,当他选择一个项目时,框字段会更新.但是,当他选择一个框时,单元格字段没有更新...

But I've a problem, for just one field dynamically updated, it's work, but no for 2 fields... Actually a user can choose a project, and when he does it, the box field is updated. But, when he choose a box, the cell field wasn't updated...

但是,我找到了一些允许它工作的东西,只需在 ->add() 中更改一些内容并反转为 ->add() 即可.但我不想要.

But, I've find something, who permit it to work, just change something in a ->add() and inversed to ->add(). But I don't want it.

我的代码是:

$builder
    ->add('project', EntityType::class, array(
        'class' => 'AppBundle\Entity\Project',
        'choice_label' => 'name',
        'placeholder' => '-- select a project --',
        'mapped' => false,
    ))
    ->add('box', EntityType::class, array(
        'class' => 'AppBundle\Entity\Box',
        'choice_label' => 'name',
        'placeholder' => '-- select a box --',
        'choices' => [],
    ))
    ->add('cell', ChoiceType::class, array(
        'placeholder' => '-- select a cell --',
    ))
;

当我将其更改为:

    builder
    ->add('box', EntityType::class, array(
        'class' => 'AppBundle\Entity\Box',
        'choice_label' => 'name',
        'placeholder' => '-- select a box --',
        //    'choices' => [],
    ))
    ->add('project', EntityType::class, array(
        'class' => 'AppBundle\Entity\Project',
        'choice_label' => 'name',
        'placeholder' => '-- select a project --',
        'mapped' => false,
    ))

    ->add('cell', ChoiceType::class, array(
        'placeholder' => '-- select a cell --',
    ))
;

它的工作......但我想要一个空的框列表在开始,我想要在框之前的项目......

It's work... But I want an empty list for box at the start, and I want project before box...

稍微精确一点,这个表单作为 CollectionType 嵌入到另一个表单中.

A little precision, this form is embded in an other form as a CollectionType.

此类型的所有代码:

    <?php

namespace AppBundle\Form;

use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class TubeType extends AbstractType
{
    /**
     * @param FormBuilderInterface $builder
     * @param array $options
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('project', EntityType::class, array(
                'class' => 'AppBundle\Entity\Project',
                'choice_label' => 'name',
                'placeholder' => '-- select a project --',
                'mapped' => false,
            ))
            ->add('box', EntityType::class, array(
                'class' => 'AppBundle\Entity\Box',
                'choice_label' => 'name',
                'placeholder' => '-- select a box --',
                'choices' => [],
            ))
            ->add('cell', ChoiceType::class, array(
                'placeholder' => '-- select a cell --',
            ))
        ;

        // MODIFIER
        $boxModifier = function (FormInterface $form, $project) {
            $boxes = (null === $project) ? [] : $project->getBoxes();

            $form->add('box', EntityType::class, array(
                'class' => 'AppBundle\Entity\Box',
                'choice_label' => 'name',
                'placeholder' => '-- select a box --',
                'choices' => $boxes,
            ));
        };

        $cellModifier = function(FormInterface $form, $box) {
            $cells = (null === $box) ? [] : $box->getEmptyCells();

            $form->add('cell', ChoiceType::class, array(
                'placeholder' => '-- select a cell --',
                'choices' => $cells,
            ));
        };

        // FORM EVENT LISTENER
        $builder->get('project')->addEventListener(
            FormEvents::POST_SUBMIT,
            function(FormEvent $event) use ($boxModifier) {
                $project = $event->getForm()->getData();

                $boxModifier($event->getForm()->getParent(), $project);
            }
        );

        $builder->get('box')->addEventListener(
            FormEvents::POST_SUBMIT,
            function(FormEvent $event) use ($cellModifier) {
                $box = $event->getForm()->getData();

                $cellModifier($event->getForm()->getParent(), $box);
            }
        );
    }

    /**
     * @param OptionsResolver $resolver
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'AppBundle\Entity\Tube'
        ));
    }
}

非常感谢您的帮助:)

推荐答案

你应该使用 $builder->addEventListener.对于多个字段,您需要做的就是在 FormEvents::PRE_SET_DATA 事件处理程序中包含动态字段.此外,如文档中所述,使用父字段数据获取子字段选择.

You should use $builder->addEventListener. For multiple fields all you need to do is to have dynamic fields inside FormEvents::PRE_SET_DATA event handler. Also, use parent field data, as explained in the doc to fetch child field choices.

我已经使用这种方法在分层字段中生成国家、州和城市实体.如果有帮助,或者您需要更多信息,请告诉我.

I have used this approach, for generating Country, State and City Entities in hierarchical fields. Let me know if it helps or you need more information.

对于更大的逻辑,您可以使用 eventSubscriber 这将使您的代码保持干净,并且您还可以将表单的动态部分重新用于其他地方.

EDIT : For bigger logic, you can use eventSubscriber which will keep your code clean and you also get to re-use dynamic part of the form for somewhere else.

对于多个依赖的层次字段,只需通过eventSubscriber类中的条件添加即可.

For multiple dependent hierarchical fields, just add them through conditions in the eventSubscriber class.

使用代码片段更新:

这里是对 Symfony 2.7 中对我有用的代码片段的演练

Here is a walk through on code snippet that worked for me in Symfony 2.7

注意:我没有按照文档中的描述替换动态 html 字段,而是通过 jQuery 我只是根据选定的父选项收集子选项并填写这些.提交后,表单会根据 eventSubscriber 上下文识别正确的子选项.所以你可以这样做:

Note : I don't replace the dynamic html field as described in the document, instead via jQuery I simply collect child options as per selected parent option and fill in those. When submitted, The form recognises correct child options as per eventSubscriber context. So here is how you might do it :

在您的父表单类型(您拥有所有 3 个字段)中调用 eventSubscriber 而不是定义这 3 个字段:

In your parent Form type (where you have all 3 fields) call a eventSubscriber instead of defining those 3 fields :

$builder->add(); // all other fields..
$builder->addEventSubscriber(new DynamicFieldsSubscriber());

按照文档中的定义创建一个 eventSubscriber,这里的文件名为 DynamicFieldsSubscriber

Create an eventSubscriber as defined in the doc, here the file name is DynamicFieldsSubscriber

<?php
namespace YourBundle\Form\EventListener;

use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Doctrine\ORM\EntityRepository;
use Symfony\Component\Form\FormInterface;

class DynamicFieldsSubscriber implements EventSubscriberInterface
{

    /**
     * Define the events we need to subscribe
     * @return type
     */
    public static function getSubscribedEvents()
    {
        return array(
            FormEvents::PRE_SET_DATA => 'preSetData', // check preSetData method below
            FormEvents::PRE_SUBMIT => 'preSubmitData', // check preSubmitData method below
        );
    }

    /**
     * Handling form fields before form renders.
     * @param FormEvent $event
     */
    public function preSetData(FormEvent $event)
    {
        $location = $event->getData();
        // Location is the main entity which is obviously form's (data_class)
        $form = $event->getForm();

        $country = "";
        $state = "";
        $district = "";

        if ($location) {
            // collect preliminary values for 3 fields.
            $country = $location->getCountry();
            $state = $location->getState();
            $district = $location->getDistrict();
        }
        // Add country field as its static.
        $form->add('country', 'entity', array(
            'class' => 'YourBundle:Country',
            'label' => 'Select Country',
            'empty_value' => ' -- Select Country -- ',
            'required' => true,
            'query_builder' => function (EntityRepository $er) {
                return $er->createQueryBuilder('c')
                        ->where('c.status = ?1')
                        ->setParameter(1, 1);
            }
        ));
        // Now add all child fields.
        $this->addStateField($form, $country);
        $this->addDistrictField($form, $state);
    }

    /**
     * Handling Form fields before form submits.
     * @param FormEvent $event
     */
    public function preSubmitData(FormEvent $event)
    {
        $form = $event->getForm();
        $data = $event->getData();

        // Here $data will be in array format.

        // Add property field if parent entity data is available.
        $country = isset($data['country']) ? $data['country'] : $data['country'];
        $state = isset($data['state']) ? $data['state'] : $data['state'];
        $district = isset($data['district']) ? $data['district'] : $data['district'];

        // Call methods to add child fields.
        $this->addStateField($form, $country);
        $this->addDistrictField($form, $state);
    }

    /**
     * Method to Add State Field. (first dynamic field.)
     * @param FormInterface $form
     * @param type $country
     */
    private function addStateField(FormInterface $form, $country = null)
    {
        $countryCode = (is_object($country)) ? $country->getCountryCode() : $country;
        // $countryCode is dynamic here, collected from the event based data flow.
        $form->add('state', 'entity', array(
            'class' => 'YourBundle:State',
            'label' => 'Select State',
            'empty_value' => ' -- Select State -- ',
            'required' => true,
            'attr' => array('class' => 'state'),
            'query_builder' => function (EntityRepository $er) use($countryCode) {
                return $er->createQueryBuilder('u')
                        ->where('u.countryCode = :countrycode')
                        ->setParameter('countrycode', $countryCode);
            }
        ));
    }

    /**
     * Method to Add District Field, (second dynamic field)
     * @param FormInterface $form
     * @param type $state
     */
    private function addDistrictField(FormInterface $form, $state = null)
    {
        $stateCode = (is_object($state)) ? $state->getStatecode() : $state;
        // $stateCode is dynamic in here collected from event based data flow.
        $form->add('district', 'entity', array(
            'class' => 'YourBundle:District',
            'label' => 'Select District',
            'empty_value' => ' -- Select District -- ',
            'required' => true,
            'attr' => array('class' => 'district'),
            'query_builder' => function (EntityRepository $er) use($stateCode) {
                return $er->createQueryBuilder('s')
                        ->where('s.stateCode = :statecode')
                        ->setParameter('statecode', $stateCode);
            }
        ));
    }
}

在此之后,您需要编写jQuery events,它应该在父选项更改时显式更新子选项,您不应该在提交表单时遇到任何错误.

After this, you need to write jQuery events which should update child options on change of parent option explicitly, You shouldn't face any error on submission of the form.

注意:上面的代码是为了在此处发布而提取和更改的.在需要时处理 namespace 和变量.

Note : The code above is extracted and changed for publishing here. Take care of namespace and variables where ever required.

这篇关于带有表单事件的提交表单的动态生成的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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