与字段无关的 Symfony 动态表单收集错误

Symfony Dynamic Form Collection Errors Not Associated to Fields

我对 Symfony 表单集合字段有疑问。我有一个父表单,其中有两个字段是表单集合。一切都工作得很好,除非我提交带有无效数据的表单。表单集合字段的错误输出在页面的表单下方。我一直在阅读有关这些字段的 error_bubbling 的文档,并意识到对于 CollectionType 字段,它默认为 true。因此,我在每个字段上将其设置为 false,但错误仍未映射到表单上的字段。

集合字段可以通过前端javascript动态添加到页面中。我注意到,在我的标记中,在我提交表单之前,有两个错误的 <div class=""form-group"> 添加到我的标记的基础上,我没有在我的模板中输出。当提交表单但无效时,将在这些 div 中输出错误。

代码;

ItemFormType;

public function buildForm(FormBuilderInterface $builder, array $options)
{
        $builder
            ->add('shop', ShopType::class, [
                'data_class' => Shop::class,
                'label' => false,
            ])
            ->add('purchase', PurchaseType::class, [
                'data_class' => Purchase::class,
                'label' => false,
            ])
            ->add('missing_items', CollectionType::class, [
                'entry_type' => MissingItemFormType::class,
                'allow_add' => true,
                'allow_delete' => true,
                'label' => false,
                'prototype' => true,
                'error_bubbling' => false,
            ])
            ->add('replaced_items', CollectionType::class, [
                'entry_type' => ReplacedItemFormType::class,
                'allow_add' => true,
                'allow_delete' => true,
                'label' => false,
                'prototype' => true,
                'error_bubbling' => false,
            ])
            ->add('submit', SubmitType::class)
            ->getForm();
    }

    /**
     * Get the form name.
     *
     * @return string
     */
    public function getName(): string
    {
        return 'missing_form';
    }

    /**
     * Set form options.
     *
     * @param OptionsResolver $resolver
     *
     * @return void
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => null,
            'error_bubbling' => false
        ]);
    }

控制器;

/**
     * @Route("/", name="homepage")
     *
     * @param ClaimMailer $mailer
     * @param Request $request
     *
     * @return Response
     */
    public function indexAction(ClaimMailer $mailer, Request $request): Response
    {
        $purchase = [
            'shop' => new Shop(),
            'purchase' => new Purchase(),
        ];

        $form = $this->createForm(MissingFormType::class, $purchase);

        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            $this->store($form, $purchase);

            // Send confirmation email.
            $mailer->send(
                $purchase['purchase']->getEmail(),
                $purchase['shop']->getName(),
                $purchase['purchase']->getClaimReferenceNumber()
            );

            return $this->render('form/form_complete.html.twig', [
                'purchase_id' => $purchase['purchase']->getPurchaseReferenceNumber(),
            ]);
        }

        return $this->render('form/purchase_form.html.twig', [
            'form' => $form->createView(),
        ]);
    }

    /**
     * Store form data.
     *
     * @param Form $form
     * @param array$claim
     *
     * @return void
     */
    public function store(Form $form, $purchase){}

模板;

{% block _missing_form_missing_items_entry_row %}
    {% for field in form %}
        <td>
            {{ form_row(field) }}
        </td>
    {% endfor %}
{% endblock %}

{% block _missing_form_replaced_items_entry_row %}
    {% for field in form %}
        <td>
            {{ form_row(field) }}
        </td>
    {% endfor %}
{% endblock %}

{% block website_body %}
    <div class="row">
        <div class="col-lg-12">
            <div class="panel panel-default">
                <div class="panel-body">
                    {{ form_start(form) }}
                    <div class="row">
                        <div class="col-lg-6">
                            {{ form_row(form.shop.name) }}
                            {{ form_row(form.shop.accountNumber) }}
                            {{ form_row(form.shop.email) }}
                            {{ form_row(form.shop.addressLine1) }}
                        </div>
                        <div class="col-lg-6">
                            {{ form_row(form.shop.addressLine2) }}
                            {{ form_row(form.shop.town) }}
                            {{ form_row(form.shop.county) }}
                            {{ form_row(form.shop.postcode) }}
                        </div>
                    </div>
                    <div class="row">
                        <h3>Missing Items</h3>
                        <table class="table missing_items">
                            <tbody class="missing_items" data-prototype="{{ form_row(form.missing_items.vars.prototype)|e('html_attr') }}"></tbody>
                        </table>
                    </div>
                    <div class="row">
                        <div class="col-lg-6">
                            {{ form_row(form.purchase.receivedReplacement) }}
                        </div>
                        <table class="table replacement-items">
                            <tbody class="replacement_items" data-prototype="{{ form_row(form.replaced_items.vars.prototype)|e('html_attr') }}"></tbody>
                        </table>
                    </div>
                    <div class="row">
                        <div class="col-lg-6">
                            {{ form_row(form.submit) }}
                        </div>
                    </div>
                    {{ form_end(form) }}
                    {% embed 'form/components/terms_and_conditions.html.twig' %}{% endembed %}
                </div>
            </div>
        </div>
    </div>
{% endblock %}

如有任何帮助,我们将不胜感激!尽一切努力将错误放在正确的位置。

所以对于其他人来说,我已经找到了这个难题的答案。

基本上,当我的 JS 通过 <tbody> 上的 "prototype" 属性将表单呈现到页面上时,据 Symfony 所知,我并没有明确地将字段输出到我的模板中.因此,在模板末尾调用“form_end(form)”时,项目字段的所有错误都会被吐出。

form_end(form) 在幕后调用 form_rest() 时,它基本上会输出尚未显式呈现的表单的任何字段 - 因此验证错误及其各自的字段正在在页面的表单末尾输出!

通过在模板中显式输出这些字段,错误和相关字段在表单中正确显示;

<tbody class="missing_items" data-prototype="{{ form_row(form.missing_items.vars.prototype)|e('html_attr') }}">
    {% for field in form.missing_items %}
        <tr class="item">
            <td>{{ form_row(field.quantity) }}</td>
            <td>{{ form_row(field.description) }}</td>
            <td>{{ form_row(field.invoiceNumber) }}</td>
            <td>{{ form_row(field.invoiceDate) }}</td>
            <td>{{ form_row(field.deliveryDate) }}</td>
        </tr>
    {% endfor %}
</tbody>

我希望这能帮助其他遇到同样困境的人!