代码之家  ›  专栏  ›  技术社区  ›  Lajos Arpad

如何确保Symfony表单不包含不必要的元素?

  •  1
  • Lajos Arpad  · 技术社区  · 8 年前

    我是Symfony的初学者,所以这个问题对于那些在这个框架中更有经验的人来说可能很简单。

    我正在构建表单,表单中有几种可能的项目类型。目前,这些是:

    • 文本
    • html
    • 形象

    然而,未来将有更多的项目。以前,项目将所有项生成到它们的位置(文本、html和图像),并隐藏特定表单项不需要的那些项(最多需要一个)。然而,我打算避免添加我不需要的项目。因为我现在还不知道 buildForm 无论是文本、html还是图像,都会运行一个任意项目,因此此时会添加所有这些项目(我知道这是违反直觉的,但这是我尝试重构的代码):

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('layoutTypeInput', TextType::class);
    
        $builder->add('blockTypeOutput', EntityType::class, array(
            'class' => 'MyPageBundle:BlockTypeOutput',
            'choice_label' => 'titleHu',
            'required' => false,
            'placeholder' => 'Válassz blokk kimenetet!',
            'empty_data' => null,
        ));
    
        $builder->add('text', TextType::class, $this->getBlockTypeOptions('text'));
    
        $builder->add('html', TextareaType::class, $this->getBlockTypeOptions('html'));
    
        $builder->add('image', ImageSelectType::class, $this->getBlockTypeOptions('image'));
    
        $builder->addEventListener(FormEvents::POST_SET_DATA, array($this, 'onPostSetData'));
        $builder->addEventListener(FormEvents::PRE_SUBMIT, array($this, 'onPreSubmit'));
    }
    

    现在,我有一个函数,可以删除 form :

    private function removeUnnecessaryItems(\Symfony\Component\Form\FormInterface $form, $key)
    {
        $keys = ['text', 'html', 'image'];
        foreach ($keys as $k) {
            if ($key !== $k) $form->remove($k);
        }
    }
    

    和内部 onPostSetData 我这样称呼它:

        $this->removeUnnecessaryItems($form, $inputObject->getLayoutTypeIdText());
    

    最后,在细枝中,我确定应该生成什么到表单中:

                {% for ioLayoutBlock in form.ioLayoutBlocks %}
                    <div class="row">
                        <div class="col-xs-12 col-md-3">
                            {{ form_errors(ioLayoutBlock.layoutTypeInput) }}
                            {{ioLayoutBlock.layoutTypeInput.vars.label}}
                        </div>
                        {{ form_widget(ioLayoutBlock.layoutTypeInput, {'attr' : {'class':'hidden'}}) }}
                        <div class="col-xs-12 col-sm-6 col-md-5">
                            {{ form_errors(ioLayoutBlock.blockTypeOutput) }}
                            {{ form_widget(ioLayoutBlock.blockTypeOutput, {'attr' : {'class':'blockTypeOutput'}}) }}
                        </div>
                        <div class="col-xs-12 col-sm-6 col-md-4">
                            {% if ioLayoutBlock.text is defined %}
                                {{ form_errors(ioLayoutBlock.text) }}
                                {{ form_widget(ioLayoutBlock.text, {'attr':{'class':'hidden uniqueInput ioLayoutBlock_text' }}) }}
                            {% elseif ioLayoutBlock.html is defined %}
                                {{ form_errors(ioLayoutBlock.html) }}
                                {% if layout.layoutType.name == 'userHTML' %}
                                    <div class="input-group ioLayoutBlock_html hidden">
                                        <a class="input-group-addon myAdminForm" target="_blank" data-my-href="page/{{ page.id }}/wysiwyg/{{ layout.id }}"><span class="glyphicon glyphicon-pencil"></span></a>
                                        {{ form_widget(ioLayoutBlock.html, {'attr':{'class':'uniqueInput wysiwyg' }}) }}
                                    </div>
                                {% else %}
                                    {{ form_widget(ioLayoutBlock.html, {'attr':{'class':'hidden uniqueInput wysiwyg ioLayoutBlock_html' }}) }}
                                {% endif %}
                            {% elseif ioLayoutBlock.image is defined %}
                                {{ form_errors(ioLayoutBlock.image) }}
                                {{ form_widget(ioLayoutBlock.image, {'attr':{'class':'hidden uniqueInput ioLayoutBlock_image' }}) }}
                            {% endif %}
                        </div>
                    </div>
                {% endfor %}
    

    如果我加载页面,所有内容都会正确显示,但不幸的是,当我尝试提交 类型 ,它给出的误差为

    此表单不应包含额外字段。

    很多倍,很多倍 类型 我拥有的物品。如果我在 removeUnnecessaryItems 在…内 onPostSetData 然后从细枝中删除条件,如:

    {% if ioLayoutBlock.text is defined %}
    

    然后一切都正常了,但这就是重构之前的工作方式。理想情况下,我希望避免在 构建表单 ,但我不知道如何在那里加载任何有意义的数据来确定项目的类型。或者,我想确保在我确实知道项目类型的活动中成功删除项目,而不会出现上述提交时的表单错误。所以,我的问题是:如何避免在表单中生成各种不必要的内容,而不被提交错误阻止?

    2 回复  |  直到 8 年前
        1
  •  2
  •   goto    8 年前

    我的方法是为表单提供一个参数

    $form = $this->createForm(DynamicType::class, $user, [
        'layout_type_id' => $layoutTypeIdText,
    ]),
    

    然后根据参数添加字段

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $this->layout_type = $options['LayoutTypeId'];
        // [...]
        if ($this->layout_type !== 'text' )
            $builder->add('text', TextType::class, $this->getBlockTypeOptions('text'));
            // [...]
        ;
    }
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            // [...]
            'layout_type_id' => null,
        ]);
    }
    

    使用这种方法,好的部分是不需要在twig中复制逻辑,表单会得到它所需的字段,因此可以使用

    {{ form_start(form) }}
    {{ form_widget(form) }}
    {{ form_end(form) }}
    

    对于您的特定情况,您需要避免在buildForm中添加项,在onPreSubmit中处理默认空值(因为如果未在buildForm中添加项,则不会调用转换器),并在onPreSubmit中添加有效项。

        2
  •  1
  •   romaricdrigon    8 年前

    您使用表单监听器是正确的,它们是您用例的方法。 我看到2个子场景I:

    • 表单数据来自模型,您可以在控制器中填充它
    • 一个新项目被添加到表单客户端(通过原型)。

    我从您的示例代码中了解到,用户不能动态添加新项目。如果您想这样做,代码只是略有不同。

    仍然是第一个场景。诀窍不是删除,而是在 PRE_SET_DATA 侦听器:

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('layoutTypeInput', TextType::class);
    
        $builder->add('blockTypeOutput', EntityType::class, array(
            'class' => 'MyPageBundle:BlockTypeOutput',
            'choice_label' => 'titleHu',
            'required' => false,
            'placeholder' => 'Válassz blokk kimenetet!',
            'empty_data' => null,
        ));
    
        $builder->addEventListener(FormEvents:: PRE_SET_DATA, array($this, 'onPreSetData'));
        // ...
    }
    
    public function onPreSetData(FormEvent $event)
    {
        $data = $event->getData(); // This contains model data (ie., from controller)
        $form = $event->getForm();
    
        $type = 'image'; // Read type from your model
    
        $formType = $this->getFormTypeForType($type);
    
        $builder->add($type, formType, $this->getBlockTypeOptions($type));
    }
    
    private function getFormTypeForType($type)
    {
        switch ($type) {
            case 'image':
                return ImageSelectType::class;
            // ...
            default:
                // Up to you, you can decide on setting a default type or enforcing that the type is correct
                throw new \RuntimeException('Unsupported type');
        }
    }
    

    使用该代码,您可以保持相同的细枝。

    我不知道你想干什么 layoutTypeInput blockTypeOutput . 也许我们在这里只回答了部分问题,请毫不犹豫地发布完整的用例。