我花了一段时间浏览 symfony2 文档,试图找到一种合适的方法来完成我需要做的事情,也许我找错了地方。
基本上,我有一个名为Album
的实体,它可以有许多与其关联的Subalbums
。当用户使用表单创建 Album
实体时,我希望他们能够内嵌快速创建 Subalbum
实体,稍后将保存该实体。我还想以自定义格式显示该字段,因此我不想使用带有 multiple
属性的 select
标记,而是手动将其呈现在Twig(我对此没有任何问题)。
Subalbum
上的关系定义如下:
/**
* @ORM\ManyToOne(targetEntity="Vhoto\AlbumBundle\Entity\Album")
* @ORM\JoinColumn(name="parent_id", referencedColumnName="id")
*/
protected $parent;
这是我迄今为止尝试过的...
在表单构建器中使用
entity
类型字段,然后手动输出该字段。我使用entity
字段时遇到的问题是,如果用户创建内联Subalbum
,当我提交表单时 symfony2 不喜欢它,因为它没有 ID。使用隐藏字段类型并尝试在同一字段名称 (
album[subalbums][]
) 下提交多个条目。当我提交表单时,Symfony2 也不喜欢这样
我想我的 Album
实体中必须有一个 prePersist
方法来创建用户拥有的任何 Subalbum
实体内联创建?
希望有一个更优雅的解决方案,但我完全忽略了。
如果有任何不清楚的地方,请告诉我。
最佳答案
确实有更好的方法。
实体创建
创建两个实体
POPO,并将多对一
关系分配给子实体的其中一个字段(您已正确完成此操作)。您可能还想在父级中定义一对多
关系
/**
* @var ArrayCollection
*
* @ORM\OneToMany(targetEntity="Child", mappedBy="parent", cascade={"persist", "remove" }, orphanRemoval=true)
*/
protected $children;
我不确定是否有必要,但为了确定起见,您应该在 setter 中显式设置关系。例如在您拥有的实体中:
public function addChild(ChildInterface $child)
{
if(!$this->hasChild($child))
{
$this->children->add($child);
$child->setParent($this);
}
}
Doctrine
可能不会使用这些方法来绑定(bind)发布数据,但自己拥有这个方法可能会解决几个持续存在的问题。
表单类型创建
为两个实体创建表单类型
/**
* This would be the form type for your sub-albums.
*/
class ChildType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
//$builder->add(...);
//...
}
public function getDefaultOptions(array $options) {
return array(
'data_class' => 'Acme\Bundle\DemoBundle\Entity\Child'
);
}
public function getName()
{
return 'ChildType';
}
}
/**
* This would be the form type for your albums.
*/
class ParentType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
// This part here describes the relationship between the two
// form types.
$builder->add('children', 'collection', array(
'type' => new ChildType(),
'allow_add' => true,
'allow_delete' => true,
'prototype' => true
));
}
public function getName()
{
return 'ChildType';
}
}
使用选项allow_add
和allow_delete
,您已经有效地告诉Symfony,用户可以从集合中添加或删除实体。 prototype
选项可让您在页面上拥有所谓的子表单的原型(prototype)。
Controller
为了安全起见,您也应该在此处强制执行该关系。我已将其隐藏在一个单独的实体管理层中(对于更复杂的实体,我有单独的管理器),但您当然也可以在 Controller 中执行此操作。
foreach($parent->getChildren() as $child)
{
if($child->getParent() === NULL)
{
$child->setParent($parent);
}
}
查看
准备表单模板。应通过在模板中的某处调用 form_rest(form)
来呈现原型(prototype)。如果没有或者您想自定义原型(prototype),请参阅以下示例,了解如何执行此操作。
<script id="ParentType_children_prototype" type="text/html">
<li class="custom_prototype_sample">
<div class="content grid_11 alpha">
{{ form_widget(form.children.get('prototype').field1) }}
{{ form_widget(form.children.get('prototype').field2) }}
{{ form_rest(form.children.get('prototype') ) }}
</div>
</li>
</script>
您必须使用 JavaScript 使表单动态化。如果您使用jQuery
,则可以通过调用$('ParentType_children_prototype').html()
来访问原型(prototype)。在向父级添加新子级时,将原型(prototype)中出现的所有 $$name$$
替换为正确的索引号非常重要。
我希望这会有所帮助。
编辑我刚刚注意到有一个 article in the Symfony2 Form Type reference关于CollectionType
。关于如何为此实现前端,它有一个很好的替代方案。
关于php - Symfony2 + Twig : Using an entity type field to store unsaved entities,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8474041/