简体   繁体   中英

Symfony forms | Dynamic collection type values

I'm building a small tool on symfony 3.4, I'm experiencing two issue with a form that I cannot find a solution for.

For the context, the form that is causing me some difficulties is based on a doctrine entity : Event. This event reference another entity : a Doctrine (nothing do to with the ORM). A doctrine references multiples Fittings. For a given Event with a given Doctrine, I want to display a collectiontype built from the doctrine fittings that expose a number meant to be the required number of this fitting for this event.

This lead to 3 entities in my form : the event itself, the doctrine, and a collectiontype of fittingRequirements built on my end.

这是它的外观

The right panel content is meant to change each time the doctrine change.

Here is the EventType :

<?php

namespace AppBundle\Form;

use AppBundle\Entity\Doctrine;
use AppBundle\Entity\Event;
use AppBundle\Form\DataTransformer\FittingRequirementTransformer;
use Doctrine\ORM\EntityRepository;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\DateTimeType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class EventType extends AbstractType
{
    protected $requirementTransformer;

    public function __construct(FittingRequirementTransformer $transformer)
    {
        $this->requirementTransformer = $transformer;
    }

    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {

        $builder
            ->setMethod('POST')
            ->add('name')
            ->add(
                'date',
                DateTimeType::class,
                [
                    'widget' => 'single_text',
                    'format' => 'yyyy-MM-dd HH:mm',
                ]
            )
            ->add('startLocation')
            ->add(
                'eventType',
                ChoiceType::class,
                [
                    'choices' => [
                        'PvE'   => 'PvE',
                        'PvP'   => 'PvP',
                        'Other' => 'Other',
                    ],
                ]
            )
            ->add('target')
            ->add('description')
            ->add(
                'doctrine',
                EntityType::class,
                [
                    'class'         => 'AppBundle\Entity\Doctrine',
                    'choice_label'  => 'name',
                    'query_builder' => function (EntityRepository $repository) {
                        return $repository->createQueryBuilder('d')->orderBy('d.name', 'ASC');
                    },
                    'required'      => false,
                ]
            );


        $formModifier = function (FormInterface $form, Doctrine $doctrine = null, Event $event) {

            $eventRequirements = [];

            if ($doctrine) {

                $doctrineFittings     = $doctrine->getFittings();
                $doctrineRequirements = $event->getDoctrineFittingRequirements($doctrine);

                $eventRequirements = $this->requirementTransformer->dataToForm(
                    $doctrineFittings,
                    $doctrineRequirements,
                    $event
                );

            }

            $form->add(
                'eventRequirements',
                CollectionType::class,
                [
                    'entry_type'    => FittingRequirementType::class,
                    'label'         => false,
                    'entry_options' => ['label' => false],
                    'data'          => $eventRequirements,
                    'mapped'        => false,
                ]
            );

        };

        $builder->addEventListener(
            FormEvents::PRE_SET_DATA,
            function (FormEvent $event) use ($formModifier) {

                $formupEvent = $event->getData();
                $formModifier($event->getForm(), $formupEvent->getDoctrine(), $formupEvent);
            }
        );

        $builder->get('doctrine')->addEventListener(
            FormEvents::POST_SUBMIT,
            function (FormEvent $event) use ($formModifier) {

                $eventForm = $event->getForm()->getParent();
                $doctrine  = $event->getForm()->getData();
                $formModifier($event->getForm()->getParent(), $doctrine, $eventForm->getData());
            }
        );
    }

    /**
     * {@inheritdoc}
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(
            [
                'data_class' => 'AppBundle\Entity\Event',
            ]
        );
    }

    /**
     * {@inheritdoc}
     */
    public function getBlockPrefix()
    {
        return 'event';
    }
}

I'm building the list of eventFittingRequirements and adding it on PRE_SET_DATA and POST_SUBMIT As you can see, I use a CollectionType of FittingRequirementType you can see bellow :

<?php

namespace AppBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\NumberType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class FittingRequirementType extends AbstractType
{
    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('number', NumberType::class);
    }

    /**
     * {@inheritdoc}
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'AppBundle\Entity\FittingRequirement'
        ));
    }

    /**
     * {@inheritdoc}
     */
    public function getBlockPrefix()
    {
        return 'appbundle_fittingrequirement';
    }
}

This is only used to expose the number of required fittings.

All this works well when I display the form, however when I submit the form using javascript to refresh the requirement part, the field are indeed replaced, but the returned form has no value in the inputs.

The $eventRequirements variable from my $formModifier contains a proper set of data , with the number value. However, when I check the XHR with the symfony profiler, the form has no values , even if I select the original doctrine again. I don't understand what is going on and how to fix this.

Thanks for reading

I just found out what was going on and fixed my issue.

My forms are fine, however the handleRequest method clears the unmapped fields I set with my custom fittingRequirement list.

I had to manually submit my form with the clearmissing parameter to false like bellow :

$form->submit($request->request->get($form->getName()), false);

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