简体   繁体   中英

Symfony Dynamic Form Collection Errors Not Associated to Fields

I have an issue with a Symfony Form Collection field. I have a parent form which has two fields which are form collections. Everything is working absolutely fine, except when I submit my form with invalid data. The errors for the form collection fields are output beneath the form on the page. I have been reading through the documentation for error_bubbling on these fields and realise that for CollectionType fields, it defaults to true. I have therefore set it to false on each field and still the errors are not mapped to the fields on the form.

The collection fields are able to be added dynamically to the page through javascript on the front end. What I have noticed, is that in my markup, before I have even submitted the form, there are two erroneous <div class=""form-group"> 's added to the base of my markup which I am not outputting in my template. When the form is submitted and is not valid, then the errors are being output within these divs.

The code;

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
        ]);
    }

Controller;

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

The Template;

{% 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 %}

Any assistance would be greatly appreciated! Tried everything to get the errors in the correct place.

So for anyone else out there, I have found the answer to this conundrum.

Basically, whilst my JS was rendering the form onto the page via the "prototype" attribute on my <tbody> , as far as Symfony was aware, I was not explicitly outputting the fields into my template. As a result, all of the errors for the item fields were being spat out when calling ' form_end(form) ' at the end of the template.

As form_end(form) calls form_rest() behind the scenes, it basically outputs any of the fields for the form which have not been explicitly rendered - as a result the validation errors and their respective fields were being output at the end of the form on the page!

By explicitly outputting these fields within the template, the errors and associated fields were displayed correctly in the form as so;

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

I do hope this helps somebody else out there who finds themselves in the same predicament!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM