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

发布于 2024-12-21 05:58:40 字数 1000 浏览 2 评论 0原文

我花了一段时间浏览 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][]) 下提交多个条目。当我提交表单时,Symfony2 也不喜欢这样

我想我必须在我的 Album 实体中使用 prePersist 方法来创建任何 Subalbum 用户创建的内联实体?

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

如果有任何不清楚的地方请告诉我。

I've spent a while looking through the symfony2 docs trying to find a suitable method of doing what I need to do, maybe I'm looking in the wrong place.

Basically, I have an entity called Album, which can have many Subalbums associated with it. As the user is creating an Album entity using a form, I want them to be able to create quick Subalbum entities in-line, which will get saved later on. I also want to display the field in a custom format, so I don't want to use a select tag with a multiple attribute, instead I'm manually rendering it in Twig (I haven't had a problem with this).

The relationship on Subalbum is defined as follows:

/**
 * @ORM\ManyToOne(targetEntity="Vhoto\AlbumBundle\Entity\Album")
 * @ORM\JoinColumn(name="parent_id", referencedColumnName="id")
 */
protected $parent;

Here's what I've tried so far...

  1. Use an entity type field in the form builder, and then manually output the field. The issue I have with using the entity field is that if the user creates a Subalbum inline, symfony2 doesn't like it when I submit the form because it has no ID.

  2. Use a hidden field type and try submitting multiple entries under the same field name (album[subalbums][]). Symfony2 also doesn't like this when I submit the form

I guess I'm going to have to have a prePersist method in my Album entity to create any Subalbum entities that the user has created inline?

Hopefully there is a far more graceful solution that I'm just completely overlooking.

Let me know if anything is unclear.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

最近可好 2024-12-28 05:58:40

确实有更好的方法来做到这一点。

实体创建

创建两个实体 POPO,并将多对一关系分配给子实体的其中一个字段(您已完成这是正确的)。您可能还想在父级中定义一对多关系,

/**
 * @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$$ 替换为正确的索引号非常重要。

我希望这有帮助。

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

There is indeed a better way to do it.

Entity creation

Create the two Entity POPOs and assign a many-to-one relationship to one of the fields of the child entity (you have done this correctly). You might also want to define a one-to-many relationship in the parent

/**
 * @var ArrayCollection
 *
 * @ORM\OneToMany(targetEntity="Child", mappedBy="parent", cascade={"persist", "remove" }, orphanRemoval=true)
 */
protected $children;

I am not sure if it's necessary, but you should explicitly set the relationship in your setters just to be sure. For example in your owning entity:

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

Doctrine doesn't probably use these methods to bind the post data, but having this for yourself might take care of several persisting issues.

Form type creation

Create a form type for both entities

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

With the options allow_add and allow_delete you've effectively told Symfony, that a user can add or remove entities from the collection. The prototype option lets you have a prototype of the so-called sub-form on your page.

Controller

You should enforce the relationship here as well just to be safe. I have tucked this away in a separate entity manager layer (I have separate managers for more complicated entities), but you can most certainly do this in your controllers as well.

foreach($parent->getChildren() as $child)
{
    if($child->getParent() === NULL)
    {
        $child->setParent($parent);
    }
}

View

Prepare your form's template. The prototype should be rendered by calling form_rest(form) somewhere in your template. In case it doesn't or you'd like to customize the prototype, here's a sample of how to do it.

<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>

You'd have to make the form dynamic by using JavaScript. If you use jQuery, you can access the prototype by calling $('ParentType_children_prototype').html(). It is important to replace all occurrences of $$name$$ in the prototype with the proper index number when adding a new child to the parent.

I hope this helps.

EDIT I just noticed there's an article in the Symfony2 Form Type reference about CollectionType. It has a nice alternative on how to implement the front-end for this.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文