Symfony2 + Twig:使用实体类型字段来存储未保存的实体


Symfony2 + Twig: Using an entity type field to store unsaved entities

我花了一段时间查看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;

这是我到目前为止所尝试的…

  1. 在表单生成器中使用entity类型的字段,然后手动输出该字段。我使用entity字段的问题是,如果用户创建一个Subalbum内联,symfony2不喜欢它,当我提交的形式,因为它没有ID。

  2. 使用隐藏字段类型并尝试在相同字段名称(album[subalbums][])下提交多个条目。当我提交表单

  3. 时,Symfony2也不喜欢这个

我想我将不得不有一个prePersist方法在我的Album实体创建任何Subalbum实体,用户已经创建内联?

希望有一个更优雅的解决方案,我只是完全忽略了。

确实有更好的方法。

实体创建

创建两个Entity popo并将many-to-one关系分配给子实体的一个字段(您已经正确地完成了此操作)。您可能还想在父

中定义one-to-many关系。
/**
 * @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可能不会使用这些方法来绑定post数据,但是为自己使用这些方法可能会解决几个持续存在的问题。

表单类型创建

为两个实体创建一个表单类型

/**
 * 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_addallow_delete选项,您已经有效地告诉Symfony,用户可以从集合中添加或删除实体。prototype选项让您在页面上拥有所谓的子表单的原型。

控制器

为了安全起见,这里也应该强制执行关系。我把它藏在一个单独的实体管理器层中(我为更复杂的实体有单独的管理器),但你当然也可以在你的控制器中这样做。

foreach($parent->getChildren() as $child)
{
    if($child->getParent() === NULL)
    {
        $child->setParent($parent);
    }
}
<<p> 视图/strong>

准备表单模板。原型应该通过在模板中的某处调用form_rest(form)来呈现。如果它没有,或者你想自定义原型,这里有一个例子,如何做到这一点。

<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()来访问原型。在向父对象添加新子对象时,用适当的索引号替换原型中出现的所有$$name$$是很重要的。

我希望这对你有帮助。

EDIT我刚刚注意到Symfony2表单类型参考中有一篇关于CollectionType的文章。对于如何实现前端,它有一个很好的替代方案。