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

时间:2011-12-12 12:05:31

标签: php symfony twig

我花了一段时间浏览symfony2文档试图找到一种合适的方法来做我需要做的事情,也许我正在寻找错误的地方。

基本上,我有一个名为Album的实体,它可以与许多Subalbums相关联。当用户使用表单创建Album实体时,我希望他们能够在线创建快速Subalbum实体,稍后将保存这些实体。我还希望以自定义格式显示字段,因此我不想使用带有select属性的multiple标记,而是使用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个实体?

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

    如果有什么不清楚,请告诉我。

1 个答案:

答案 0 :(得分:3)

确实有更好的方法。

实体创建

创建两个Entity POPO,并为子实体的其中一个字段分配many-to-one关系(您已正确完成此操作)。您可能还想在父

中定义one-to-many关系
/**
 * @var ArrayCollection
 *
 * @ORM\OneToMany(targetEntity="Child", mappedBy="parent", cascade={"persist", "remove" }, orphanRemoval=true)
 */
protected $children;

我不确定是否有必要,但您应该明确设置您的安装者中的关系以确保。例如,在您拥有的实体中:

public function addChild(ChildInterface $child)
{
    if(!$this->hasChild($child))
    {
        $this->children->add($child);
        $child->setParent($this);
    }
}

Doctrine可能不会使用这些方法绑定帖子数据,但是这样做可能会解决几个持续存在的问题。

表单类型创建

为两个实体创建表单类型

/**
 * 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);
    }
}

查看

准备表单的模板。应该通过在模板中的某个地方调用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$$非常重要。

我希望这会有所帮助。

编辑我刚注意到CollectionType有一个article in the Symfony2 Form Type reference。它有一个关于如何为此实现前端的很好的替代方案。