【问题标题】:How to make sure that a Symfony form does not contain unnecessary elements?如何确保 Symfony 表单不包含不必要的元素?
【发布时间】:2018-02-26 10:46:03
【问题描述】:

我是 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());

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

            {% 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 %}

如果我加载页面,一切都会正确显示,但不幸的是,当我尝试提交 form 时,它给出了错误

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

我拥有的 form 项目数是我的数倍。如果我在 onPostSetData 内注释掉对 removeUnnecessaryItems 的调用,然后从树枝中删除条件,例如:

{% if ioLayoutBlock.text is defined %}

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

【问题讨论】:

  • 是否有域规则要求您在创建表单后删除表单项?或者它只是按照它已经完成的方式?
  • @goto 我认为它已经完成了,但我对项目和 Symfony 都是新手,所以我可能会遗漏一些东西。

标签: php symfony symfony-2.8


【解决方案1】:

我的方法是给你的表单一个参数

$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 添加有效项目.

【讨论】:

  • 感谢您的回答。我理解你的想法,但是我在 Symfony 中太菜鸟了,所以我可能需要一段时间来尝试一下,包括但不限于找到创建表单的位置所需的时间(而不是通过调用 createForm)。总的来说,我的速度要快得多,但是对于 Smyfony,我恳请您耐心等待,然后才能给您任何反馈。
  • @LajosArpad 没问题,如果需要,请联系我
  • 你的想法很好,但在我的具体情况下不是一个选择。我需要避免在 buildForm 添加项目,在 onPreSubmit 处理默认的空值(因为如果项目未在 buildForm 添加,则不会调用转换器)并在 onPreSubmit 添加有效项目。请注意,我的树枝上没有重复,因为我需要在文本、html 和图像表单项的情况下以不同的方式显示值。我赞成你的回答,如果你编辑它并添加我在这里提供的信息,我会接受它。
【解决方案2】:

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

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

我从您的示例代码中得知用户无法动态添加新项目。如果你想这样做,代码只是略有不同。

仍然是第一种情况。诀窍不是删除,而是在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');
    }
}

使用该代码,您可以保留相同的 Twig。

我不确定你想用layoutTypeInputblockTypeOutput 做什么。也许我们在这里只是部分回答,请不要犹豫发布完整的用例。

【讨论】:

  • 这是一个正确的答案,但由于之前给出了另一个答案并进行了编辑以反映我的情况,我不得不接受它。但您的解决方案同样出色。
猜你喜欢
  • 2019-03-26
  • 2015-10-10
  • 2018-11-08
  • 2011-09-30
  • 1970-01-01
  • 1970-01-01
  • 2013-03-17
  • 1970-01-01
  • 2012-04-27
相关资源
最近更新 更多