在 Symfony 2.8、3.0 及更高版本中将数据传递给 buildForm()

Posted

技术标签:

【中文标题】在 Symfony 2.8、3.0 及更高版本中将数据传递给 buildForm()【英文标题】:Passing data to buildForm() in Symfony 2.8, 3.0 and above 【发布时间】:2016-03-05 19:22:14 【问题描述】:

我的应用程序当前使用构造函数将数据传递给我的表单类型,正如this answer 中所建议的那样。但是Symfony 2.8 upgrade guide 建议不推荐将类型实例传递给createForm 函数:

将类型实例传递给 Form::add()、FormBuilder::add() 和 FormFactory::create*() 方法已弃用,将不受支持 Symfony 3.0 中不再存在。传递完全限定的类名 而是输入。

Before:    
$form = $this->createForm(new MyType());

After:
$form = $this->createForm(MyType::class);

看到我无法使用完全限定的类名传递数据,是否有替代方法?

【问题讨论】:

你需要传递什么样的数据?是可以注射的东西吗? 希望 UPGRADE.md 得到改进:github.com/symfony/symfony/issues/18662 【参考方案1】:

这也破坏了我们的一些表格。我通过选项解析器传递自定义数据来修复它。

在您的表单类型中:

public function buildForm(FormBuilderInterface $builder, array $options)

    $this->traitChoices = $options['trait_choices'];

    $builder
        ...
        ->add('figure_type', ChoiceType::class, [
            'choices' => $this->traitChoices,
        ])
        ...
    ;


public function configureOptions(OptionsResolver $resolver)

    $resolver->setDefaults([
        'trait_choices' => null,
    ]);

然后,当您在控制器中创建表单时,将其作为选项而不是在构造函数中传递:

$form = $this->createForm(ProfileEditType::class, $profile, [
    'trait_choices' => $traitChoices,
]);

【讨论】:

刚刚也遇到了这个问题并做了类似的解决方案。我认为如果需要数据并且如果您想要执行通常在构造函数定义中执行的类型提示,您应该在 configureOptions() 中为选项解析器使用 setRequired() 和 setAllowedTypes() 方法, 而不是 setDefaults()。 这正是你应该做的。 :) @Roubi 你做同样的事情,你在 configureOptions 方法中定义一个选项,然后在添加表单字段时传递它。 我对这个变化也不满意。不过感谢您的回答。 FormTypes 就像工厂一样,它们应该是无状态的。通过它们的构造函数(而不是通过服务标签方法)注入值使其成为有状态的。这样,您就有了一种创建表单类型的统一方式。选项总是用来代替构造函数参数。此更改对 DX 和可用性非常有用。【参考方案2】:

以下是使用 Symfony 3 的任何人将数据传递到嵌入式表单的方法。首先完全按照上面提到的 @sekl 进行操作,然后执行以下操作:

在您的主要 FormType 中

使用 'entry_options' 将 var 传递给嵌入式表单

->add('your_embedded_field', CollectionType::class, array(
          'entry_type' => YourEntityType::class,
          'entry_options' => array(
            'var' => $this->var
          )))

在您的嵌入式 FormType 中

将选项添加到 optionsResolver

public function configureOptions(OptionsResolver $resolver)

    $resolver->setDefaults(array(
        'data_class' => 'Yourbundle\Entity\YourEntity',
        'var' => null
    ));

访问 buildForm 函数中的变量。请记住在构建器函数之前设置此变量。就我而言,我需要根据特定 ID 过滤选项。

public function buildForm(FormBuilderInterface $builder, array $options)

    $this->var = $options['var'];

    $builder
        ->add('your_field', EntityType::class, array(
          'class' => 'YourBundle:YourClass',
          'query_builder' => function ($er) 
              return $er->createQueryBuilder('u')
                ->join('u.entity', 'up')
                ->where('up.id = :var')
                ->setParameter("var", $this->var);
           ))
     ;

【讨论】:

为了减少混淆 - $this->var 是您要传递的值,不一定来自类变量。【参考方案3】:

这里可以使用另一种方法 - 为检索数据注入服务。

    将您的表单描述为服务 (cookbook) 将受保护的字段和构造函数添加到表单类 使用注入对象获取您需要的任何数据

例子:

services:
    app.any.manager:
        class: AppBundle\Service\AnyManager

    form.my.type:
        class: AppBundle\Form\MyType
        arguments: ["@app.any.manager"]
        tags: [ name: form.type ]

<?php

namespace AppBundle\Form;

use AppBundle\Service\AnyManager;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class MyType extends AbstractType 

    /**
     * @var AnyManager
     */
    protected $manager;

    /**
     * MyType constructor.
     * @param AnyManager $manager
     */
    public function __construct(AnyManager $manager) 
        $this->manager = $manager;
    

    public function buildForm(FormBuilderInterface $builder, array $options) 
        $choices = $this->manager->getSomeData();

        $builder
            ->add('type', ChoiceType::class, [
                'choices' => $choices
            ])
        ;
    

    public function configureOptions(OptionsResolver $resolver) 
        $resolver->setDefaults([
            'data_class' => 'AppBundle\Entity\MyData'
        ]);
    


【讨论】:

这很好,但是当参数对服务管理器不可用时将不起作用。【参考方案4】:

如果有人使用 form.factory 服务中的“createNamedBuilder”或“createNamed”函数,这里是关于如何使用它设置和保存数据的 sn-p。您不能使用“数据”字段(保留为空),您必须将传递的数据/实体设置为 $options 值。

我还合并了有关使用 setAllowedTypes() 和 setRequired() 选项的 @sarahg 说明,它似乎工作正常,但您首先需要使用 setDefined() 定义字段

如果您需要设置数据,请记住将其添加到“数据”字段中。

在控制器中,我使用 getBlockPrefix,因为 getName 在 2.8/3.0 中已弃用

控制器:

/*
* @var $builder Symfony\Component\Form\FormBuilderInterface
*/
$formTicket = $this->get('form.factory')->createNamed($tasksPerformedForm->getBlockPrefix(), TaskAddToTicket::class, null, array('ticket'=>$ticket) );

表格:

public function configureOptions(OptionsResolver $resolver)    
    $resolver->setDefined('ticket');
    $resolver->setRequired('ticket');
    $resolver->addAllowedTypes('ticket', Ticket::class);

    $resolver->setDefaults(array(           
        'translation_domain'=>'AcmeForm',
        'validation_groups'=>array('validation_group_001'),
        'tasks' => null,
        'ticket' => null,
    ));


 public function buildForm(FormBuilderInterface $builder, array $options)   

    $this->setTicket($options['ticket']);
    //This is required to set data inside the form!
    $options['data']['ticket']=$options['ticket'];

    $builder

        ->add('ticket',  HiddenType::class, array(
                'data_class'=>'acme\TicketBundle\Entity\Ticket',
            )
        )
...

【讨论】:

以上是关于在 Symfony 2.8、3.0 及更高版本中将数据传递给 buildForm()的主要内容,如果未能解决你的问题,请参考以下文章

在 SF3.4 及更高版本中运行 Symfony DIC 烟雾测试

Android:BitmapFactory.nativeDecodeAsset 处的 OutOfMemoryError(Honeycomb 3.0 及更高版本)

如何在 Android 3.0 及更高版本中更改 ActionBar 的触摸效果颜色

如何在 Android 3.0 及更高版本中更改 ActionBar 的触摸效果颜色

Linq 查询在 ASP.NET-Core 3.0 及更高版本中对数字等字符串进行排序

错误:Android Gradle 插件仅支持 Kotlin Gradle 插件版本 1.3.0 及更高版本