9

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

/* 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 ......

4

5 回答 5

23

要了解这里发生了什么,您必须首先了解数据映射。你打电话时

$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);
于 2013-09-18T17:42:44.227 回答
3

正如 Bernhard 所指出的,侦听器是执行此操作的唯一方法,因为数据在子表单中尚不可用。我使用 eventListener 来解决类似的要求。以下是我的代码的简化版本,希望对您有所帮助:

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

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

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 中做所有事情的想法对我来说是陌生的(而且似乎不必要地复杂)。但我希望一旦我更好地理解了这个框架,它看起来会更直观。正如这个例子所表明的,使用事件侦听器并不复杂,您只需将代码包装在该闭包中(或将其放入它自己的单独函数中,如文档中所述)。

我希望对某人有所帮助!

于 2014-05-09T23:22:44.957 回答
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']);
        }
    }
}
于 2014-09-09T15:30:39.890 回答
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.
            }
        );

    }

但是......如果你想用它做点什么,你需要确保你测试空值,因为在数据实际动态创建之前和之后多次触发事件。例如,如果您想即时修改表单,例如添加某种提交按钮:

/*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,它通常在持久化期间由学说或类似自动生成。因此,为了在持久化之前将提交按钮或类似实体与集合中的该实体相关联,您可能必须使“名称”字段唯一或为实体创建一个单独的字段以保存唯一类型参数和在持久化之前以独特的方式生成它,以便在表单创建期间将诸如提交按钮之类的东西关联到实体。

于 2018-10-25T20:18:19.603 回答
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,所以数据在那里可用。在此示例中,我使用它来更改集合中每个项目的表单行的标签。查看可能的 vars 列表

于 2017-04-08T15:21:10.390 回答