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


87

按照此答案中的建议,我的应用程序当前使用构造函数将数据传递到表单类型。但是,Symfony 2.8升级指南建议不要将类型实例传递给createForm函数:

将类型实例传递给Form :: add(),FormBuilder :: add()和FormFactory :: create *()方法已过时,在Symfony 3.0中不再受支持。而是传递该类型的标准类名。

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

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

看来我无法使用完全合格的类名传递数据,还有其他选择吗?


1
您需要传递哪种数据?它可以注入吗?
塞拉德2015年

2
希望UPGRADE.md得到改善:github.com/symfony/symfony/issues/18662
althaus

Answers:


133

这也破坏了我们的某些形式。我通过将自定义数据通过选项解析器传递来修复了该问题。

在您的表单中输入:

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,
]);

8
刚才也遇到了这个问题,并做了类似的解决方案。我认为,如果数据是必需的,并且您想要执行通常在构造函数定义中进行的类型提示,则应将setRequired()和setAllowedTypes()方法用于configureOptions()中的选项解析器,而不是setDefaults()。
sarahg 2015年

2
那正是你应该做的。:)
Bernhard Schussek

3
@Roubi也是一样,您在configureOptions方法中定义一个选项,然后在添加表单字段时将其传递。
Bart Wesselink,

2
我也不满意这种变化。谢谢你的回答。
Adambean '16

2
FormTypes的行为就像工厂,它们应该是无状态的。通过其构造函数(而不是通过service标签方法)注入值使其具有状态。这样,您就有1种统一的方式来创建表单类型。总是要使用选项而不是构造函数参数。此更改对于DX和可用性非常有用。
任何人

6

这里可以使用另一种方法-注入服务以获取数据。

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

例:

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'
        ]);
    }

}

很好,但是当参数对服务管理器不可用时将不起作用。
demonkoryu

5

如果有人在使用来自form.factory服务的'createNamedBuilder'或'createNamed'函数,则以下是如何使用它设置和保存数据的代码段。您不能使用“数据”字段(保留为空),而必须将传递的数据/实体设置为$options值。

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

同样在表单内部,如果您需要设置数据,请记住将其添加到“数据”字段中。

在Controller中,我使用getBlockPrefix,因为2.8 / 3.0中已弃用getName

控制器:

/*
* @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',
            )
        )
...
}

5

这是使用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是您要传递的值,不一定要从类变量传递。
Darius.V,
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.