phpsymfonysonata

Sonata admin PRE_SUBMIT form event makes admin twig variable null


i am having problems with form events in an admin class. I have dynamically added choices in a custom entity type.Choices are added to the database by javascript via a controller.To be able to validate those dynamic choices (added after the form is renderd), i added a PRE_SUBMIT form event to the field in the admin class.So before the form is submitted i load the new choice list from db and remove and add the field back to the form to update its options, as shown here https://gist.github.com/webdevilopers/fef9e296e77bb879d138

In the twig template of the form type i use the sonata_admin.admin variable to get needed data. The problem is when there is an error in the form (on any other field anywhere in the form) the sonata_admin.admin variable form the twig template is null.

thanks for the help

admin::configureFormFields()

$class = 'LibrinfoVarietiesBundle:SelectChoice';
    $repo = $this->getConfigurationPool()->getContainer()->get('doctrine.orm.entity_manager')->getRepository($class);
    $admin = $this;
    $formBuilder = $formMapper->getFormBuilder();

    $formBuilder->addEventListener(FormEvents::SUBMIT, function ($event) use ($formBuilder, $admin, $repo, $class) {
        $form = $event->getForm();
        $subject = $admin->getSubject($event->getData());
        $label = 'librinfo_varieties_admin_variety_regulatory_status_librinfo_core_customchoice';
        $choices = $repo->findBy(array(
            'label' => $label
        ));

        $choiceViews = array();
        foreach ($choices as $choice)
        {
            $choiceViews[$choice->getvalue()] = $choice;
        }
        if ($form->has('regulatory_status'))
        {
            $form->remove('regulatory_status');
        }

        $customChoice = $formBuilder->getFormFactory()->createNamed('regulatory_status', 'librinfo_customchoice', null, array(
            'choices' => $choices,
            'class' => $class,
            'auto_initialize' => false,
        ));
        $form->add($customChoice);
    }); 

form type ConfigureOptions()

$label = 'librinfo_varieties_admin_variety_regulatory_status_librinfo_core_customchoice';
     $choices = $this->repo->findBy(array(
                'label' => $label
            ));
     $choiceViews = array();
     foreach ($choices as $choice)
     {
         $choiceViews[$choice->getvalue()] = $choice;
     }

    $resolver->setDefaults(array(
        'choice_label' => 'value',
        'class'        => 'LibrinfoVarietiesBundle:SelectChoice',
        'placeholder'  => '', 
        'choices'      => $choiceViews,
    ));

twig block

{% block librinfo_customchoice_widget %}
{% set subject = sonata_admin.admin.subject %}
{% spaceless %}
    {{ block('choice_widget') }}
    {% if subject.fieldset is defined %}
        <a id="{{ subject.fieldset }}_{{ subject.field }}" class="add-choice editable editable-click inline-input" href="#"><i class="fa fa-plus-circle"></i></a>
    {% else %}
        <a id="{{ block_prefixes.4 }}" class="add-choice editable editable-click inline-input" href="#"><i class="fa fa-plus-circle"></i></a>
    {% endif %}
{% endspaceless %}
{% endblock %}

Solution

  • This is because the Sonata FormTypeFieldExtension is not called when you directly add field to the form object trough events.

    But I found a workaround, you have to add the field with the FormMapper first. This will setup sonata admin options.

    Then, on your event callback, get back the current options of the field and merge them with your own:

    protected function configureFormFields(FormMapper $formMapper)
    {
        $formMapper
            ->add('date', null, [
                'widget' => 'single_text',
            ])
            ->add('method', EnumType::class, [
                'class' => PaymentMethod::class,
                'translation_domain' => 'global',
                'prefix_label_with_class' => true,
                'attr' => [
                    'class' => 'payment-method',
                ],
            ])
            ->add('reference', TextType::class, [
                'required' => false,
                'attr' => [
                    'class' => 'payment-reference',
                ]
            ])
            ->add('amount')
        ;
    
        $builder = $formMapper->getFormBuilder();
        $factory = $builder->getFormFactory();
    
        $referenceTypeMaker = function (FormEvent $event) use ($factory) {
            $form = $event->getForm();
            $data = $event->getData();
    
            $paymentMethod = $data instanceof Payment ? $data->getMethod() : $data['method'];
    
            if (PaymentMethod::CREDIT_NOTE === $paymentMethod) {
                $form->add($factory->createNamed('reference', CreditNoteRefType::class, null, array_merge(
                    $form->get('reference')->getConfig()->getOptions(), // Put back original options here.
                    [
                        'auto_initialize' => false
                    ]
                )));
            }
        };
    
        $builder->addEventListener(FormEvents::PRE_SET_DATA, $referenceTypeMaker);
        $builder->addEventListener(FormEvents::PRE_SUBMIT, $referenceTypeMaker);
    }
    

    As you can see on my code sample, the reference field is a TextType but can be a CreditNoteRefType depending on the payment method choice.

    Putting back the already defined field options to the new one solve the issue.