我是Symfony的初学者,所以对于那些在这个框架中经验丰富的人来说,这个问题可能很简单。
我正在构建表单,表单中有几种可能的项目类型。此时这些是:
但是,将来还会有更多项目。以前,项目会将所有项目生成到它们的位置(文本,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 %}
如果我加载页面,所有内容都会正确显示,但不幸的是,当我尝试提交form
时,会出现错误
此表单不应包含额外字段。
我拥有的form
件物品数量是其中之多。如果我在removeUnnecessaryItems
内的onPostSetData
上发出评论,然后从树枝中移除条件,例如:
{% if ioLayoutBlock.text is defined %}
然后一切正常,但这就是它在重构之前的工作方式。理想情况下,我想避免在buildForm
添加太多不必要的东西,但我不知道如何在那里加载任何有意义的数据来确定项目的类型。或者,我想确保在我知道其类型的事件中成功删除这些项目,而不会在上面提到的提交中出现表单错误。所以,我的问题是:如何在不被提交错误阻止的情况下,避免在表单中生成各种不必要的内容?
答案 0 :(得分:2)
我的方法是为表单提供一个参数
$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,
]);
}
有了这个approch,好的部分是你不要在树枝上复制你的逻辑,表单得到它需要的字段,所以你可以使用
渲染表单{{ form_start(form) }}
{{ form_widget(form) }}
{{ form_end(form) }}
对于您的特定情况,您需要避免在buildForm中添加项目,以处理onPreSubmit中的默认空值(因为如果未在buildForm中添加项目,则不会调用变换器)并在onPreSubmit中添加有效项目。
答案 1 :(得分:1)
您使用表单侦听器是正确的,它们是您的用例的方法。 我看到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。
我不确定您要对layoutTypeInput
和blockTypeOutput
做些什么。也许我们在这里只是部分回答,不要在发布完整用例时犹豫不决。