我有一个包含集合的表单。所以我有:
/* 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 ......
答案 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通常在持久性期间由教义或类似方法自动生成。因此,为了在持久化集合之前将此提交按钮或与此实体中的该实体相关联,您可能必须使“名称”字段唯一,或为实体创建一个单独的字段以保存唯一的类型参数,并在持久化之前以独特的方式生成它,以便在表单创建过程中将诸如“提交”按钮之类的内容与实体相关联。