简体   繁体   中英

Is it possible to dynamically set data_class in configureOptions?

I'm trying to refactor some bad code, currently I have over 20 forms (dictionaries) with a single field called name and two similar forms (dictionaries) with extra fields.

These forms are being embedded as a collection in another form, where entry_type is dynamically set to one of the former forms, based on value returned from my factory.

The purpose was to modify selects during edition of some other forms, so the user could freely add or remove options with new button/delete buttons.

I've tried to remove my 20 forms by creating a base form with a single field - name and configuring data_class in configureOptions dynamically but I couldn't find a way to do that. When I've tried to modify a constructor and set the value there, I couldn't access the constructor during createForm - I can only pass options, but options aren't accessible in configureOptions .

I was able to find that it was possible in older version of symfony via $this->createForm(new FormType($option))

Is it possible to do a similar thing in symfony 5? If not, what are the workarounds?

If I can improve the question anyhow, please let me know. Here is the code:

Action:

/**
 * @Route("/dictionary/getForm/{id}",
 *     name="dictionary_form")
 * @param $id
 */    
public function getDictionaryView(Request $request, EntityManagerInterface $em, $id){

    $repository = $em->getRepository('App:'.substr($id, 3));
    $items = $repository->findAll();
    
    $form = $this->createForm(DictionaryCollectionType::class,['dictionary' => $items],array(
        'type' => DictionaryFormFactory::createForm($id),
        'action' => $id,
    ));

    $form->handleRequest($request);

    if($form->isSubmitted() && $form->isValid()){

        $data = $form->getData()['dictionary'];
        $idsForm = array_map(function($item) {return $item->getId();},$data);
        foreach($items as $item) {
            if(!in_array($item->getId(),$idsForm)) $em->remove($item);
        }

        foreach($data as $entity) {
            $em->persist($entity);
        }

        $em->flush();

        $return = [];
        foreach($data as $entity) {
            $append = ['value' => $entity->getId(), 'name' => $entity->getName()];
            if($entity instanceof DegreesDisciplines) $append['field'] = $entity->getField()->getId();
            $return[] = $append;
        }

        return new JsonResponse($return);

    }

    return $this->render('Admin\Contents\dictionary.html.twig', [
        'form' => $form->createView()
    ]);
}

(Idea of) Base form:

<?php

namespace App\Form\Dictionaries;

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

class NewDictionaryType extends AbstractType {

    private $data_class;

    public function __construct($data_class)
    {
        $this->data_class = $data_class;
    }
    
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('name', TextType::class, [
            'label' => 'Nazwa',
        ]);
    }

    /**
     * @param OptionsResolver $resolver
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => $this->data_class,
        ]);
    }
}

Example form that repeats. Basically, only 'data_class' changes in other forms:

<?php

namespace App\Form\Dictionaries;

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

class NewNoticesTypeType extends AbstractType {
    
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('name', TextType::class, [
            'label' => 'Nazwa',
            'required' => false,
        ]);
    }

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

Parent form:

<?php

namespace App\Form;

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

class DictionaryCollectionType extends AbstractType {

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('dictionary', CollectionType::class, [
            'entry_type' => $options['type'],
            'entry_options' => array('label' => false),
            'empty_data' => null,
            'allow_add'    => true,
            'allow_delete' => true,
            'label' => false,
        ])
        ->add('save', SubmitType::class, [
            'attr' => ['class' => 'save btn btn-success mt-2', 'data-toggle' => 'modal', 'data-target' => '#dictionaryBackdrop', 'action' => $options['action']],
        ]);
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => null,
            'type' => null,
            'action' => null
        ));
    }
}

Don't use a constructor to pass configuration options, but just use createForm to pass options:

$form = $this->createForm(DictionaryCollectionType::class,['dictionary' => $items],array(
    'type' => DictionaryFormFactory::createForm($id),
    'action' => $id,
    'data_class' => $dataClass // <-- put your data class (FQCN) here
));

See Symfony's documentation about Passing Options to Forms

One more thing you probably should refactor is 'App:'.substr($id, 3) . It's better to use a FQCN instead of the old Bundle:Entity format.

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