如何在Symfony2中为表单字段设置默认值?

147

有没有一种简单的方法为文本表单字段设置默认值?


1
是的,但是这个问题中给出的答案并不令人满意/不能正常工作...我会添加一个“编辑”来解释原因 :-) - herrjeh42
看起来你想要的“完美”解决方案是让一个字段拥有“default_value”选项。问题是,目前它没有,所以我认为你正在寻找的完美解决方案目前不存在。Symfony提供的唯一东西(参见链接)就是数据选项。因此,if-then是我目前能看到的唯一方法。即使字段本身有一个“default_value”选项,我想它在内部也会做同样的事情。 - crysallus
1
@Crone 这个问题是2年前提出的。 - Ondrej Slinták
1
@OndrejSlinták 我没有投票关闭任何一个重复的问题,但是提醒一下:无论哪个问题先出现,都不重要。如果新问题更好或者有更好的答案,那么请投票将旧问题作为新问题的重复 - Jeff Puckett
显示剩余2条评论
22个回答

118

你可以使用empty_data设置默认值。

$builder->add('myField', 'number', ['empty_data' => 'Default value'])

30
设置数据并不意味着设置默认值。这个答案是正确的。 - Alexei Tenitski
11
只有在提交时没有值的情况下,它似乎才将字段设置为1。那么当你希望表单默认显示1并且没有任何值时,该怎么办? - Brian
在我的测试中,empty_data 不允许我覆盖从一个空字段提交的默认值,例如,如果您想将其保存到数据库中作为 0 而不是 NULL。据我所知,这个 bug 仍然存在:https://github.com/symfony/symfony/issues/5906 - Chadwick Meyer
1
这并没有回答问题;'empty_data' 只有在表单字段为空时才会在表单提交后保存指定的值。它不会为文本表单字段设置默认值... 它根本不会触及表单字段,因此该字段将显示为空。 - SteveExdia

113

可以在创建过程中轻松使用:

->add('myfield', 'text', array(
     'label' => 'Field',
     'empty_data' => 'Default value'
))

12
对于Symfony 2.1,我需要将'data'键更改为'value' - Edd
180
这不仅设置了默认值,而且在任何情况下都会强制使用该值。我不认为这是所谓的“默认值”... - Hubert Perron
4
我对这个解决方案进行了负评,因为它并不是问题的解决方案(正如Hubert Perron在上面提到的)。我正在尝试在此帖子http://stackoverflow.com/questions/17986481/set-default-values-using-form-classes-in-symfony-2中获得更好的解决方案。 - herrjeh42
13
这是初始值, 默认值为 empty_data - Pierre de LESPINAY
4
“data” 是无用的 - 它会覆盖已保存的值。“empty_data” 不显示该值,它在空值提交时使用该值,并使得无法保存未选中的选项。 - moldcraft
显示剩余3条评论

65

我之前思考过几次这个问题,所以想记录一下我所想到/使用的不同方法。其中可能有些有用,但都不是Symfony2的“完美”解决方案。

构造函数 在实体中,你可以使用 $this->setBar('默认值');但这将在每次加载实体时(无论是否来自数据库)调用,有点混乱。不过它适用于各种字段类型,因为你可以创建日期或其他需要的内容。

在 get 函数内使用 if 语句 我不会这样做,但你可以这样做。

return ( ! $this->hasFoo() ) ? 'default' : $this->foo;

工厂/实例。调用一个静态函数/辅助类,它会为你提供一个预先填充了数据的默认实体。例如:

function getFactory() {
    $obj = new static();
    $obj->setBar('foo');
    $obj->setFoo('bar');

   return $obj;
}

这种方法并不是理想的,因为如果您添加额外的字段,则需要维护此函数,但它确实意味着您正在分离数据设置器/默认值和从数据库生成的内容。类似地,如果您想要不同的默认数据,则可以拥有多个getFactories。

扩展/反射实体 创建一个扩展实体(例如FooCreate extends Foo),在创建时通过构造函数获取默认数据。与Factory / instance思想类似,只是一种不同的方法 - 我个人更喜欢静态方法。

在构建表单之前设置数据 在构造函数/服务中,您知道是否有新实体或者它是从数据库中填充的。因此,在获取新实体时调用不同字段上的setData是可能的。例如:

if( ! $entity->isFromDB() ) {
     $entity->setBar('default');
     $entity->setDate( date('Y-m-d');
     ...
}
$form = $this->createForm(...)

表单事件 在创建表单时,您可以设置字段的默认数据。如果您想覆盖默认数据,可以使用PreSetData事件侦听器。但这样做会导致表单工作负担增加、代码重复,同时也会使代码难以维护和理解。

扩展表单 与表单事件类似,但是根据是否为数据库/新实体调用不同类型。我的意思是,您可以定义FooType来定义编辑表单,然后BarType扩展FooType并将所有数据设置到字段中。在控制器中,您只需选择要实例化的表单类型即可。这种方法对于自定义主题来说不够理想,并且与事件一样会增加过多的代码维护成本。

Twig 您可以基于每个字段使用value选项创建自己的主题并设置默认数据。如果希望保持模板整洁并且使表单可重用,则可以将此包装到表单主题中。例如:

form_widget(form.foo, {attr: { value : default } });

JS 如果字段为空,用JS函数填充表单很容易。例如,您可以使用占位符。但是这是一个非常糟糕的想法。

表单作为服务 对于我做过的一个大型基于表单的项目,我创建了一个生成所有表单、执行所有处理等任务的服务。这是因为这些表单将在多个控制器和多个环境中使用,虽然这些表单以相同的方式生成/处理,但它们的显示/交互方式不同(例如,错误处理、重定向等)。这种方法的美妙之处在于,您可以设置默认数据,执行所需的所有操作,以及通用处理错误等,而所有内容都封装在一个地方。

结论 在我看来,你会一次又一次地遇到相同的问题——默认数据应存放在哪里?

  • 如果将其存储在数据库/Doctrine级别上,如果你不想每次都存储默认值会发生什么?
  • 如果将其存储在实体级别上,如果您想在其他地方重复使用该实体而没有任何数据怎么办?
  • 如果将其存储在实体级别上并添加一个新字段,是否希望以前的版本在编辑时有该默认值?在数据库中的默认值也是如此...
  • 如果将其存储在表单级别上,当您以后维护代码时是否很明显?
  • 如果它在构造函数中,如果您在多个地方使用表单会发生什么?
  • 如果将其推送到JS级别,那么您走得太远了——数据不应该在视图中(我们忽略兼容性、渲染错误等)
  • 如果像我一样在多个地方使用,则服务非常好,但对于一个站点上的简单添加/编辑表单来说,这是过度设计...

为此,我每次都会以不同的方式解决问题。例如,在创建表单之前,可以在构造函数中轻松(并且逻辑清晰地)设置注册表单的“新闻通讯”选项。当我构建链接在一起的表单集合时(例如,不同表单类型中的哪些单选按钮链接在一起),我使用事件监听器。当我构建更复杂的实体(例如,需要子项或大量默认数据的实体)时,我使用函数(例如,“getFactory”)根据需要创建元素。

我认为没有一个“正确”的方法,因为每次遇到此要求时情况略有不同。

祝你好运!希望我的回答能给你提供一些思路,而且希望我没有说得太多。


你可以再详细解释一下“生成所有表格的服务”是什么意思吗?我目前也在做一个以表单为中心的项目,不同角度的看法对我来说会非常有帮助。 - user2268997
2
当使用Doctrine时,当实体从数据库加载时,构造函数不会被调用。 - NDM
您的参考资料很棒,但现在我必须通过谷歌搜索来寻找并希望找到您所说的内容......我请求您添加链接,引用文档或您从中获取建议的任何来源,例如表单事件或扩展表格。 - SteveExdia

48

如果您需要设置默认值并且表单与实体相关,则应使用以下方法:

// buildForm() method
public function buildForm(FormBuilderInterface $builder, array $options) {
    $builder
    ...
    ->add(
        'myField',
        'text',
        array(
            'data' => isset($options['data']) ? $options['data']->getMyField() : 'my default value'
        )
    );
}
否则,myField 将始终被设置为默认值,而不是从实体获取值。

如果是数组而不是实体,请将$options['data']->getMyField()替换为$option['data']['myField'] - ggg
4
我认为这是添加或更新的正确方法。但我讨厌Symfony使它变得过于复杂。 - Yarco
1
这是唯一正确的答案。当我查看文档时,我不理解其他答案。empty_data:此选项确定字段在提交值为空时将返回什么值。它不设置初始值。 - Vincent Decaux
我正在查看的代码是通过Sonata FormMapper构建表单的,没有任何$ options变量传递,所以我不知道如何获取你在这里谈论的内容。 - SteveExdia

19

16
如果你的表单绑定到一个实体上,只需使用构造方法在实体本身上设置默认值:
public function __construct()
{
    $this->field = 'default value';
}

即便如此,您的表单仍可以具有不映射到实体的附加字段('mapped' => false)。对于这些字段,请使用 setData(...) - Dizzley
这仅适用于创建新记录,而不适用于编辑现有记录。当打开编辑表单时,默认属性值完全被忽略或甚至未经检查。 - SteveExdia

12

方法1(来源于http://www.cranespud.com/blog/dead-simple-default-values-on-symfony2-forms/

只需在实体中设置默认值,可以在变量声明或构造函数中进行设置:

class Entity {
    private $color = '#0000FF';
    ...
}
或者
class Entity {
    private $color;

    public function __construct(){
         $this->color = '#0000FF';
         ...
    }
    ...
}
从上面链接的评论中采用方法2,还要根据 Dmitriy 在 How to set default value for form field in Symfony2? 中的回答(不是被接受的答案),在使用FormBuilder添加字段时将默认值添加到数据属性中。
请注意,这假定属性只有在它是一个新实体而不是现有实体时,才会将其设置为null。
public function buildForm(FormBuilderInterface $builder, array $options) {
    $builder->add('color', 'text', array(
            'label' => 'Color:',
            'data' => (isset($options['data']) && $options['data']->getColor() !== null) ? $options['data']->getColor() : '#0000FF'
        )
    );
}

第一个有效(谢谢!),第二个对我无效:$options["data]总是被设置,因此默认值永远不会被使用。我仍然想知道解决方案1是否是预期的方法... - herrjeh42
你关于$options['data']总是被设置正确。如果你没有初始化实体字段,你可以在字段上测试null,例如:'data' => $options['data']->getColor() !== null ? etc... 这假设null不是颜色字段的有效值,因此现有实体永远不会对该字段具有null值。 - crysallus
啊,我太蠢了:我尝试使用'isset($options['data']->getColor())',但是我收到了一个关于“不允许在写入上下文中使用”的错误消息,并忘记了我必须以不同的方式检查它 :-) - herrjeh42
1
实际上,有时候数据输入并没有设置。更安全的做法是同时测试,即isset($options['data']) && $options['data']->getColor() !== null ? ... - crysallus

10

不要使用:

'data' => 'Default value'

阅读这里:https://symfony.com/doc/current/reference/forms/types/form.html#data

"当渲染表单时,数据选项始终覆盖从域数据(对象)获取的值。这意味着,当表单编辑已经持久化的对象时,对象值也会被覆盖,导致在提交表单时失去其持久化的值。"


使用以下代码:

假设有一个实体Foo,并且有一个字段“active”(在此示例中是CheckBoxType,但处理方式与其他类型相同),您想要默认选中该字段。

在您的FooFormType类中添加:

...
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
...
public function buildForm( FormBuilderInterface $builder, array $options )
{
    ...

    $builder->add('active', CheckboxType::class, array(
        'label' => 'Active',
    ));

    $builder->addEventListener(
        FormEvents::PRE_SET_DATA,
        function(FormEvent $event){                 
            $foo = $event->getData();
            // Set Active to true (checked) if form is "create new" ($foo->active = null)
            if(is_null($foo->getActive())) $foo->setActive(true);
        }
   );
}
public function configureOptions( OptionsResolver $resolver )
{
    $resolver->setDefaults(array(
        'data_class' => 'AppBundle:Foo',
    ));
}

这里是金钱!!使用表单事件监听器在默认值之前检查您的值。这应该是在表单中设置默认值的被接受答案,因为它适用于新建和编辑操作。 - tlorens
这是处理此问题的正确方式,应该被接受为答案。 - Bettinz
如果使用条件/三元运算符,那么你在开头提到的并不正确。可以像这样写:'data' => $data['myfield'] ?? '默认值' - xarlymg89

10

通过使用没有类的表单或者在需要访问任何服务来设置默认值的情况下,可以为任何案例/方法提供通用解决方案:

// src/Form/Extension/DefaultFormTypeExtension.php

class DefaultFormTypeExtension extends AbstractTypeExtension
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        if (null !== $options['default']) {
            $builder->addEventListener(
                FormEvents::PRE_SET_DATA,
                function (FormEvent $event) use ($options) {
                    if (null === $event->getData()) {
                        $event->setData($options['default']);
                    }
                }
            );
        }
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefault('default', null);
    }

    public function getExtendedType()
    {
        return FormType::class;
    }
}

并注册表单扩展:

app.form_type_extension:
    class: App\Form\Extension\DefaultFormTypeExtension
    tags:
        - { name: form.type_extension, extended_type: Symfony\Component\Form\Extension\Core\Type\FormType }

之后,我们可以在任何表单字段中使用default选项:

$formBuilder->add('user', null, array('default' => $this->getUser()));
$formBuilder->add('foo', null, array('default' => 'bar'));

1
这应该被接受为最佳答案(最新的)。 - medunes

9
您可以像这样设置表单message的默认值:
$defaultData = array('message' => 'Type your message here');
$form = $this->createFormBuilder($defaultData)
    ->add('name', 'text')
    ->add('email', 'email')
    ->add('message', 'textarea')
    ->add('send', 'submit')
    ->getForm();

如果您的表单映射到实体,则可以这样操作(例如,默认用户名):
$user = new User();
$user->setUsername('John Doe');

$form = $this->createFormBuilder($user)
    ->add('username')
    ->getForm();

2
我更喜欢这种方法,特别是在大多数应用程序中,你都是创建一个表单并传入一个实体,表单处理该实体。 - skrilled

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接