symfony 2.3表单getData在子表单集合中不起作用

时间:2013-09-18 11:20:12

标签: forms symfony collections builder

我有一个包含集合的表单。所以我有:

/* my type */
public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
    ->add('name')
    ->add('photos','collection',array(
        'type'=> new PhotoType(),
        'allow_add'=>true));
}

/*Photo Type*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
    ->add('photoname')
    ->add('size')
}

但我想访问照片中的数据,所以我尝试了PhotoType:

$data = $builder->getData();

但它似乎无法正常工作,即使我正在编辑表单,因此照片集也有数据。 为什么我不能以另一个调用的形式访问$ builder-> getData()?因为我正在尝试不做和eventListener ......

5 个答案:

答案 0 :(得分:21)

要了解这里发生的事情,您必须先了解数据映射。当你打电话

$form->setData(array('photoname' => 'Foobar', 'size' => 500));

表单的数据映射器负责获取给定的数组(或对象)并将嵌套值写入表单的字段,即调用

$form->get('photoname')->setData('Foobar');
$form->get('size')->setData(500);

但在您的示例中,您不是处理Form,而是处理FormBuilder个对象。 FormBuilder负责收集表单的配置并使用此信息生成Form实例。因此,FormBuilder还允许您存储表单的默认数据。但由于它只是一个简单的配置对象,因此它将不会调用数据映射器。例如:

$builder = $factory->createBuilder()
    ->add('photoname')
    ->add('size')
    ->setData(array('photoname' => 'Foobar', 'size' => 500));

print_r($builder->get('photoname')->getData());
print_r($builder->get('size')->getData());

此示例将输出:

null
null

因为我们将FormBuilder转换为Form实例后会发生数据映射。我们可以使用此事实为各个字段设置单独的默认值:

$builder->add('size', null, array('data' => 100));
// which is equivalent to
$builder->get('size')
    ->setData(100)
    ->setDataLocked(true);

print_r($builder->get('photoname')->getData());
print_r($builder->get('size')->getData());

输出:

null
100    

需要数据锁定以防止数据映射器覆盖您刚存储的默认数据。如果您传递“数据”选项,则会自动完成。

最后,您将构建表单。现在,FormBuilder会在必要时调用Form::setData(),然后调用数据映射器:

$form = $builder->getForm();

// internally, the following methods are called:

// 1) because of the default data configured for the "size" field
$form->get('size')->setData(100);

// 2) because of the default data configured for the main form
$form->setData(array('photoname' => 'Foobar', 'size' => 500));

// 2a) as a result of data mapping
$form->get('photoname')->setData('Foobar');

// 2b) as a result of data mapping (but ignored, because the data was locked)
$form->get('size')->setData(500);

答案 1 :(得分:3)

正如伯恩哈德指出的那样,听众是唯一的方法,因为数据尚未在子表格中提供。我使用eventListener来解决类似的需求。下面是我的代码的简化版本,希望对您有所帮助:

我有View实体的父表单,其中包含很多字段,以及其他表单的集合。其中一个子表单用于关联实体ViewVersion,实际上需要为动态实体加载另一个表单集合,该动态实体是与View关联的内容类型。此内容类型可以是许多不同类型的实体之一,例如文章,配置文件等。因此,我需要找出View数据中设置的contentType,然后找到该捆绑包的动态路径,并包括那个formType。

一旦你知道怎么做,它实际上很容易!

class ViewType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            // Basic Fields Here
            // ...
            // ->add('foo', 'text')
            // ...
            // Load a sub form type for an associated entity
            ->add('version', new ViewVersionType())
        ;
    }
}



class ViewVersionType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            // Basic Fields Here
            // ...
            // ->add('foo', 'text')
            // ...
        ;

        // In order to load the correct associated entity's formType, 
        // I need to get the form data. But it doesn't exist yet.
        // So I need to use an Event Listener
        $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
            // Get the current form
            $form = $event->getForm();
            // Get the data for this form (in this case it's the sub form's entity)
            // not the main form's entity
            $viewVersion = $event->getData();
            // Since the variables I need are in the parent entity, I have to fetch that
            $view = $viewVersion->getView();
            // Add the associated sub formType for the Content Type specified by this view
            // create a dynamic path to the formType
            $contentPath = $view->namespace_bundle.'\\Form\\Type\\'.$view->getContentType()->getBundle().'Type';
            // Add this as a sub form type
            $form->add('content', new $contentPath, array(
                'label' => false
            ));
        });

    }
}

那就是它。我是Symfony的新手,所以在EventListener中做所有事情的想法对我来说很陌生(而且似乎不必要的复杂)。但我希望一旦我更好地理解框架,它就会更直观。正如这个例子所表明的那样,用事件监听器做这件事并不复杂,你只需将你的代码包装在那个闭包中(或者把它放在它自己的独立函数中作为described in the docs)。

我希望能帮助别人!

答案 2 :(得分:0)

在提交或编辑中,您可以在将FormBuilder转换为Form实例时访问数据。对于集合类型,您可以尝试:

...
$form = $formBuilder->getForm();
...
if ($this->getRestMethod() == 'POST') {
    $form->handleRequest($this->get('request'));
    if ($form->isValid()) {
        $formData = $form->getData();
        foreach ($formData['photos'] as $key => $collectionRow) {
            var_dump($collectionRow['photoname']);
            var_dump($collectionRow['size']);
        }
    }
}

答案 3 :(得分:0)

在我的情况下,构建表单时不需要数据,但是在构建视图时(稍后)。在我的子表单类型的buildForm函数旁边,我添加了buildView函数:

namespace AppBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\Form\FormInterface;

class MyType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        // ...
    }

    public function buildView(FormView $view, FormInterface $form, array $options)
    {
        $data = $form->getData();
        $view->vars['name'] = $data->objproporwhatever;
    }

    // ...
}

因为稍后会调用buildView,所以数据可用。在这个例子中,我用它来改变集合中每个项目的表格行的标签。查看this question

答案 4 :(得分:0)

要添加到Chadwick Meyer中(在Symfony 4中,但可​​能适用于早期版本),需要使用事件侦听器来访问集合中的数据,因为许多次尚未创建和/或尚未创建数据已关联或嵌入到集合中。但是,通过事件侦听器实际获取集合中的数据存在一些复杂性,这在日常使用中变得很重要。

在照片表单构建器中,您必须包括一个事件侦听器:

/*Photo Type*/
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
        ->add('photoname')
        ->add('size');

        $builder->addEventListener(FormEvents::POST_SET_DATA,
             function (FormEvent $event)  {

                $form = $event->getForm();

                // this would be your entity
                 $photo = $event->getData();

                //Do something with the photo data.
            }
        );

    }

但是...如果您想对其进行操作,则需要确保测试是否为null,因为在实际动态创建数据之前和之后多次触发了该事件。例如,如果您想即时修改表单,例如添加某种提交按钮:

/*Photo Type*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
    ->add('photoname')
    ->add('size')


    $builder->addEventListener(FormEvents::POST_SET_DATA,
         function (FormEvent $event) use ($formModifier) {

                $form = $event->getForm();
                // this would be your entity
                 $photo = $event->getData();

                $formModifier($form,$photo);
         }
    );

    $formModifier = function (FormInterface $form, Photo $photo = null) {

        if (!empty($photo)){//Critical to do this test to avoid errors and get to events with data 
            $form->add('submitButton', SubmitType::class, array(
                 'label' => 'Do Something',
                ));
            }

    };
}

最后,请注意,在某些情况下,并非所有数据都将与特定实体相关联,直到将其实际保存在数据库中为止。例如,如果实体是新创建的,则它尚不具有其ID,该ID通常在持久性期间由教义或类似方法自动生成。因此,为了在持久化集合之前将此提交按钮或与此实体中的该实体相关联,您可能必须使“名称”字段唯一,或为实体创建一个单独的字段以保存唯一的类型参数,并在持久化之前以独特的方式生成它,以便在表单创建过程中将诸如“提交”按钮之类的内容与实体相关联。