简体   繁体   English

是否可以在 configureOptions 中动态设置 data_class?

[英]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.我正在尝试重构一些糟糕的代码,目前我有 20 多个表单(字典),其中有一个名为name字段和两个带有额外字段的类似表单(字典)。

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.这些表单被嵌入为另一种表单中的集合,其中entry_type根据我的工厂返回的值动态设置为前一种表单。

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.我试图通过创建一个带有单个字段的基本表单来删除我的 20 个表单 - name并在configureOptions动态配置data_class但我找不到方法来做到这一点。 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 .当我尝试修改构造函数并在那里设置值时,我无法在createForm期间访问构造createForm - 我只能传递选项,但在configureOptions中无法访问选项。

I was able to find that it was possible in older version of symfony via $this->createForm(new FormType($option))我能够通过$this->createForm(new FormType($option))在旧版本的 symfony 中发现它是可能的

Is it possible to do a similar thing in symfony 5?可以在 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:基本上,只有 'data_class' 其他形式的变化:

<?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:不要使用构造函数传递配置选项,而只需使用createForm传递选项:

$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请参阅 Symfony 有关将选项传递给表单的文档

One more thing you probably should refactor is 'App:'.substr($id, 3) .您可能应该重构的另一件事是'App:'.substr($id, 3) It's better to use a FQCN instead of the old Bundle:Entity format.最好使用 FQCN 而不是旧的Bundle:Entity格式。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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