如何使用Symfony 2 FormBuilder在编辑视图中禁用字段


How to disable a field in edit view using Symfony 2 FormBuilder

我用Symfony2 FormBuilder创建了一个表单,我想禁用编辑视图中的一个字段。实际上,我用包装器(display:none)隐藏了它,但我想知道是否有更好的方法。我的代码如下所示:

实体类型

public function buildForm(FormBuilderInterface $builder, array $options) {
    $builder->add('fieldToDisabledInEditView');
    // ...

实体控制器

public function newAction() {
    $entity = new Entity;
    $form = $this->createForm(new EntityType, $entity);
    // ...
}
public function editAction() {
    $entity = new Entity;
    $form = $this->createForm(new EntityType, $entity);
    // ...
}

新建(分支)模板

<form>
    {{ form_row(form.fieldToDisabledInEditView) }}
    {# ... #}

编辑(分支)模板

<form>
    <span class="theValueOfTheHiddenField">{{ entity.fieldToDisabledInEditView }}</span>
    <div style="display:none">
        {{ form_row(form.fieldToDisabledInEditView) }}
    </div>
    {# ... #}

我想您会发现创建和编辑之间还有其他区别,尤其是验证组。由于您的控制器知道正在执行哪个操作,因此请考虑创建两种表单类型EditEntity和CreateEntity,然后使用公共基来最大限度地减少重复代码@cheesemackfly展示了如何将禁用的属性添加到元素中。

当然,你可能会觉得有两种形式是浪费这么简单的区别。在这种情况下,向您的类添加一个意向标志,并在控制器中设置它

class EntityType
{
    public function __construct($intention)
    {
        $this->intention = $intention;
     ...
    // Use $this->intention to tweak the form
    }
}
// controller
$form = $this->createForm(new EntityType('create'), $entity);
OR
$form = $this->createForm(new EntityType('edit'), $entity);

如果你真的想进入其中,那么就用di来注入意图。

 // controller
 $formType = $this->get('entity.create.formtype');
 OR
 $formType = $this->get('entity.edit.formtype');

通过使用服务,您可以从一个表单类型开始,然后当您最终将其拆分为两个(您将这样做)时,您的控制器仍将像以前一样工作。

还有一件事,假设您使用不同的模板进行编辑/创建,您实际上可以直接在trick中设置禁用的属性。所以根本没有代码更改。

{{ form_row(form.yourField, { 'attr':{'disabled':'disabled'} }) }}

========================================================================更新:2016年3月3日

为了防止任何人偶然发现这一点,请注意Symfony 3不再支持一个类实现多个表单类型。基本上,您必须拥有单独的表单类型类,即使它们几乎相同。并且永远不要将实例数据添加到表单类型中。

这是可以使用表单类的事件订阅者的典型情况
在您的情况下,它应该是:

// src/Acme/DemoBundle/Form/EventListener/AddfieldToDisabledInEditViewSubscriber.php
namespace Acme'DemoBundle'Form'EventListener;
use Symfony'Component'Form'FormEvent;
use Symfony'Component'Form'FormEvents;
use Symfony'Component'EventDispatcher'EventSubscriberInterface;
class AddfieldToDisabledInEditViewSubscriber implements EventSubscriberInterface
{
    public static function getSubscribedEvents()
    {
        // Tells the dispatcher that you want to listen on the form.pre_set_data
        // event and that the preSetData method should be called.
        return array(FormEvents::PRE_SET_DATA => 'preSetData');
    }
    public function preSetData(FormEvent $event)
    {
        $data = $event->getData();
        $form = $event->getForm();
        // check if the object is "new"
        // If you didn't pass any data to the form, the data is "null".
        // This should be considered a new object
        if (!$data || !$data->getId()) {
            $form->add('fieldToDisabledInEditView');
        }
        else
        {
            $form->add('fieldToDisabledInEditView', null, array('disabled' => true));
            //If PHP >= 5.4
            //$form->add('fieldToDisabledInEditView', null, ['disabled' => true]);
        }
    }
}

在你的表格中键入:

// src/Acme/DemoBundle/Form/Type/EntityType.php
namespace Acme'DemoBundle'Form'Type;
// ...
use Acme'DemoBundle'Form'EventListener'AddfieldToDisabledInEditViewSubscriber;
class EntityType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('your_field');
        //...
        $builder->addEventSubscriber(new AddfieldToDisabledInEditViewSubscriber());
    }
    // ...
}

http://symfony.com/doc/current/cookbook/form/dynamic_form_modification.html

这种方法一点也不优雅,但我使用它是因为它很简单:

实体控制器

public function newAction() {
    $entity = new Entity;
    $form = $this->createForm(new EntityType, $entity);
    // ...
}
public function editAction() {
    $entity = new Entity;
    $form = $this->createForm(new EntityType, $entity);
    $form->remove('fieldToDisabledInEditView');    
    // ...
}

对于那些在Symfony 3中寻找解决方案而不创建单独的表单类型类(用于添加和编辑)并且不使用表单事件的人,您可以定义一个自定义选项,并在创建时将其传递给表单:

我在表单类型类中创建了一个默认值为falseis_edit选项

public function configureOptions(OptionsResolver $resolver)
{
    $resolver->setDefaults(array(
        'data_class' => SomeEntity::class,
        'is_edit' => false
    ));
}

您可以使用同一类的buildForm方法中的$options数组访问此选项:

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder->add('someField', TextType::class, array(
        'disabled' => $options['is_edit']
    ))
}

最后,您可以通过在表单创建时传递默认值来覆盖它:

$someForm = $this->createForm(
    SomeEntityType::class,
    $someEntity,
    array('is_edit' => true)
);

https://symfony.com/doc/3.4/form/form_dependencies.html

新建(分支)模板在单独呈现表单字段时不要忘记{{form_row(form_token)}}

<form>
    {{ form_row(form.fieldToDisabledInEditView) }}
    {{ form_row(form.field2) }}
    {{ form_row(form.field3) }}
    {# ... #}
    {{ form_row(form._token) }}

编辑(树枝)模板只是不要渲染{{form_row(form.fieldToDisabledInEditView)}},再次不要忘记令牌。

<form>
    {{ form_row(form.field2) }}
    {{ form_row(form.field3) }}
    {# ... #}
    {{ form_row(form._token) }}