使用 Symfony Form Builder 保存多个嵌入式表单,多个主菜在 1 层深时工作正常,在 2 层深时出错。 (一对多),

Saving multiple embedded forms using Symfony's Formbuilder, multiple entrees work fine 1 level deep, goes wrong 2 levels deep. (1-to-many),

我遇到的问题是关于 Symfony 的 (3) formbuilder。我有 3 个实体,我为其创建了 3 个 FormType。见下文;代码下方的实际问题。

我有以下实体:

DocumentBaldoc 1 -> * DocumentBaldocConnectionPoint 1 -> * DocumentBaldocAccount

我尝试实现以下目标。我想为 DocumentBaldoc 创建一个表单,我想为 DocumentBaldocConnectionPoints 创建多个表单,我想为 DocumentBaldocConnectionPointAccounts 创建多个表单。保存时,这些需要使用关系 (fk) 存储在数据库中。

我有以下 class DocumentBaldoc(表格)

namespace FooBundle\Form\Documents\Baldoc;

use FooBundle\Entity\Documents\Baldoc\DocumentBaldoc;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;

class DocumentBaldocType extends AbstractType
{
    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('document_release')
            ->add('identification')
            ->add('version')
            ->add('type')
            ->add('creationDateTime')
            ->add('validityPeriod')
            ->add('contractReference')
            ->add('contractType')
            ->add('issuerMarketParticipantIdentification')
            ->add('issuerMarketParticipantIdentificationCodingScheme')
            ->add('issuerMarketParticipantMarketRoleCode')
            ->add('recipientMarketParticipantIdentification')
            ->add('recipientMarketParticipantIdentificationCodingScheme')
            ->add('recipientMarketParticipantMarketRoleCode')
            ->add('applicationContext')
            ->add('applicationContextCodingScheme')
            ->add('connectionPoints', CollectionType::class, [
                'entry_type'   => DocumentBaldocConnectionPointType::class,
                'allow_add'    => true,
                'by_reference' => false,
            ])
            ->add('save', SubmitType::class);
    }

    /**
     * {@inheritdoc}
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => DocumentBaldoc::class
        ));
    }
}

我有以下 class 用于 DocumentBaldocConnectionPoint(表单)

namespace FooBundle\Form\Documents\Baldoc;

use FooBundle\Entity\Documents\Baldoc\DocumentBaldocConnectionPoint;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class DocumentBaldocConnectionPointType extends AbstractType
{
    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('identification')
            ->add('identificationCodingScheme')
            ->add('measureUnitCode')
            ->add('accounts', CollectionType::class, [
                'entry_type' => DocumentBaldocAccountType::class,
                'allow_add' => true,
                'by_reference' => false,
            ]);
    }

    /**
     * {@inheritdoc}
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => DocumentBaldocConnectionPoint::class
        ));
    }
}

我有以下 class 用于 DocumentBaldocAccount(表单)

namespace FooBundle\Form\Documents\Baldoc;

use FooBundle\Entity\Documents\Baldoc\DocumentBaldocAccount;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class DocumentBaldocAccountType extends AbstractType
{
    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('identification')
            ->add('identificationCodingScheme')
            ->add('accountTso')
            ->add('accountTsoCodingScheme');
    }

    /**
     * {@inheritdoc}
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => DocumentBaldocAccount::class
        ));
    }

}

我使用 Symfony 的表单构建器为 DocumentBaldoc 实体生成表单。通过添加一个 CollectionType 和 DocumentBaldocConnectionPoint 作为 entry_type 让我能够在模板中使用原型变量。

{% block DOCUMENT %}
    {{ form_start(form) }}
    <ul class="connectionPoints"
        data-prototype="{{ form_widget(form.connectionPoints.vars.prototype)|e('html_attr') }}">
        {{ form_widget(form) }}
    </ul>
    {{ form_end(form) }}
{% endblock %}

在此之后,我可以使用一些 Javascript 创建无穷无尽的 DocumentBaldocConnectionPoint 表单流,当我按下保存这些表单时(多个!!)DocumentBaldocConnectionPoints 存储在数据库中,并带有父 DocumentBaldoc 的 FK。

到目前为止,这工作得很好,但当我想为 DocumentBaldocConnectionPointAccount 做同样的事情时,问题就来了。页面加载时不存在 DocumentBaldocConnectionPoint 表单,因此我无法直接访问原型属性,我通过创建一些 Javascript 逻辑来解决此问题,该逻辑在创建 DocumentBaldocConenctionPoint 表单时触发。然后它遵循与其父级相同的逻辑。我遇到的问题是我无法保存多个 DocumentBaldocConenctionPointAccounts,它总是保存最后一个,这让我觉得过程中某处它没有保存为数组,或者数组被最后一个条目覆盖。我已经对它进行了几天的修改,但在逻辑上找不到任何差异(表单类型或实体,关系定义遵循相同的结构)这让我觉得我没有使用它,因为它应该被使用.

这些是两个实体的注释关系定义,$connectionPoints 和 $accounts 都在实体的构造函数中实例化为 ArrayCollections。

// DocumentBalcon
@ORM\OneToMany(targetEntity="DocumentBaldocConnectionPoint", mappedBy="document", cascade={"persist"})
private $connectionPoints;

// DocumentBalconConnectionPoint
@ORM\OneToMany(targetEntity="DocumentBaldocAccount", mappedBy="connectionPoint", cascade={"persist"})
private $accounts;

唯一突出的是 DocumentBaldocConnectionPoint 表单的原型是使用 __name__ 语法呈现的,需要用唯一标识符替换,并且当我抓住DocumentBaldocConnectionPointAccount 原型,我通过创建一个自己的标识符来保证唯一性来解决这个问题。

我已经阅读了有关 formbuilder 和创建此类嵌入式表单的 Symfony 文档,但该文档仅说明我正在尝试做的事情是可能的,没有示例。我不知道我正在做的是处理这些嵌入式表单的正确方法,还是我遗漏了过程中的某些步骤。

非常欢迎有关此问题的任何信息或帮助!

问题已解决。

第一个子窗体 DocumentBaldocConnectionPoint 还包含属性原型,其中包含 DocumentBaldocConnectionPointAccount 的窗体。当更改 DocumentBaldocConnectionPoint 中的 __name__ 值时,DocumentBaldocConnectionPointAccount 的值也被无意中更改,这导致了意外行为,即每个 DocumentBaldocConnectionPointAccount 表单都被放置在值为 'index' 的键上的数组中,仅更改每个 DocumentConnectionPoint。

我使用的解决方案如下:

在函数 buildForm 的 DocumentBaldocConnectionPointType class 中,我添加了值为 "accounts" 的原型键,这让我可以通过名称引用获取原型表单。

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
        ->add('identification')
        ->add('identificationCodingScheme')
        ->add('measureUnitCode')
        ->add('accounts', CollectionType::class, [
            'entry_type' => DocumentBaldocAccountType::class,
            'allow_add' => true,
            'by_reference' => false,
            'prototype' => 'accounts'
        ]);
}

在树枝模板中我添加了一个 div 如下:

<div id="prototypes"
     data-prototype-account="{{ form_widget(form.connectionPoints.vars.prototype.children['accounts'].vars.prototype) | e }}"
     data-prototype-connection-point="{{ form_widget(form.connectionPoints.vars.prototype) | e }}">
</div>

这导致 div 存储原型表单,让我可以在需要时轻松查询和使用它们。