Home > Back-end >  Symfony - Entity Type Field with Select2
Symfony - Entity Type Field with Select2

Time:04-03

I have the following problem. I have two tables in my Symony 6 project:

  1. Articles
  2. Variants

These two tables are linked to each other in that an article can have several variants and a variant always belongs to one article.

Now I have a form to create a new variant in which I have to select the corresponding article. For this I want to use the EntityType Field as a dropdown.

The problem is that I have up to 100,000 articles in the article table which causes hughe loading problems when loading all options at once. Therefore, I thought about using Select2 so that a query is only made after 1-2 characters have been entered and only the articles that match the search are displayed in the dropdown.

With a "normal" ChoiceType I managed it in another context, but I don't know how to transfer this to the EntityType.

Below is the code for the working ChoiceType:

JS:

    $(#sizes).select2({
        ajax: {
            url: {{ path("text_ajax_load_select_option") }},
            dataType: 'json',
            type: 'GET',
            data: function (params) {
    
                var queryParameters = {
                    term: params.term,
                }
                return queryParameters;
            },                
            processResults: function (data) {
                return {
                    results: $.map(data.selectOptions, function (selectOptions) {
                        return {
                            text: selectOptions.title,
                            id: selectOptions.title
                        }
                    })
                };
            }
        }
    })

Controller:

    #[Route('/ajax_load_select_options', name: 'test_ajax_load_select_option', methods: ['GET', 'POST'])]
    public function getDropdownOptions(ChoicesService $service, Request $request, SizesRepository $repository) {

            $sizes = $service->getData($sizesRepository, $request);
            return $this->json(['selectOptions' => $sizes],200); 
    } 

ChoicesService:

class ChoicesService {
    public function getData($repository, $request) {

        $searchTerm =  $request->query->get('term');

        // Get results from the Repository
        $results = $repository->getDropdownData($searchTerm); 

        return $results;
    }
}

Function in Repository:

    public function getDropdownData($searchTerm="") {

        $query = $this->createQueryBuilder($this->table);

        if($searchTerm != "") {
            $this->searchQuery .= $this->table.'.title LIKE '.'\'%'.$searchTerm.'%\'';
            $query->andWhere($this->searchQuery);
        }

        $results = $query->getQuery()->getResult();
        return $results;
    }

ArtikelType:

class ArtikelType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {

        // Create the fields for the form

        $builder
            .. other fields
            ->add('sizes', ChoiceType::class, [
                'multiple'=>true,
                'label' => "Sizes",
                'choices' => [
                ],
                'attr' => [
                    'class' => 'dropdown'
                ]   
            ])                       
        ;

        // Add EventSubscribers for specific fields (here: sizes and colors)
        $builder->addEventSubscriber(new SelectFieldListener([[$sizes="sizes",$label="Sizes"]]));
    }

EventListener:

class SelectFieldListener implements EventSubscriberInterface
{
    
    public function __construct($fields=array()) {
        $this->fields = $fields;
    }
    public static function getSubscribedEvents(): array {
        return [
            FormEvents::PRE_SET_DATA => 'onPreSetData',
            FormEvents::PRE_SUBMIT   => 'onPreSubmit',
        ];
    }

    //This Event is used to prepopulate the previous select options for all Select Fields in the corresponding form
    public function onPreSetData(FormEvent $event, $test): void
    {

        // Get the parent form
        $form = $event->getForm();
                
        // Get the data for the choice field
        $data = $event->getData();
                
        // Create the Arrays for the choices
        $choices = array();
        $choices_formatted = array();


        foreach($this->fields as $field) {
                $functionName = 'get'.ucfirst($field[0]);
                $choices[$field[0]] = $data->$functionName();

            foreach($choices[$field[0]] as $key=>$value){
                $choices_formatted[$field[0]][$value] = $value;

                $form->add(
                    $field[0],
                    ChoiceType::class,
                    [
                        'multiple'=>true,
                        'label' => $field[1],   
                        'choices'=> $choices_formatted[$field[0]],
                        'attr' => [
                            'class' => 'dropdown'
                        ] 
                    ]
                );
            }
        }
    }

    public function onPreSubmit(FormEvent $event) {

        // Get the parent form
        $form = $event->getForm();
                
        // Get the data for the choice field
        $data = $event->getData();
        
        // Create the Arrays for the choices
        $choices = array();
        $choices_formatted = array();

        foreach($this->fields as $field) {
            $choices[$field[0]] = $data[$field[0]];
            foreach($choices[$field[0]] as $key=>$value){
                $choices_formatted[$field[0]][$value] = $value;

                $form->add(
                    $field[0],
                    ChoiceType::class,
                    [
                        'multiple'=>true,   
                        'label' => $field[1], 
                        'choices'=> $choices_formatted[$field[0]],
                        'attr' => [
                            'class' => 'dropdown'
                        ] 
                    ]
                );
            }
        }    
    }
}

New Form (VariantenType) with the EntityType Dropdown to populate dynamically based on search input in select2 field:

    public function buildForm(FormBuilderInterface $builder, array $options): void
    {

        $builder
            ->add('title')
            ->add('article_no')
            ->add('description')           
            ->add('base_article', EntityType::class, [
                'class' => Artikel::class,
                'multiple' => false,
                'choice_label' => 'title',
                // 'query_builder' => function(ArtikelRepository $er) {
                //     return $er->createQueryBuilder('artikel')
                //         ->orderBy('artikel.title', 'ASC');
                // },
                'label' => 'Base Article',
            ])         
        ;
    }
    

I would be happy about any ideas and suggestions.

Best regards Schwaluck

CodePudding user response:

You just need to add a PRE_SUBMIT event listener to your FormBuilder:

public function buildForm(FormBuilderInterface $builder, array $options): void
{
      $builder
        // ... other properties         
        ->add('base_article', EntityType::class, [
            'class' => Artikel::class,
            'choice_label' => 'title', 
            'label' => 'Base Article',
            'choices' => [],
            'attr' => [
                'class' => 'select2article'
            ]  
        ]); 

       $builder->addEventListener(
            FormEvents::PRE_SUBMIT,
            function (FormEvent $event) {
                $data = $event->getData();
                $form = $event->getForm();
                if(isset($data['base_article']) and $data['base_article']!=null){
                    $selected = $data['base_article'];
                    $form->add('base_article', EntityType::class, array(
                        'class' => Artikel::class,
                        'choice_label' => 'title',
                        'label' => 'Base Article',
                        'attr' => [
                           'class' => 'select2article'
                        ],
                        'query_builder' => function (EntityRepository $er) use  ($selected){
                            return $er->createQueryBuilder('a')
                                ->where('a.id = :id')
                                ->setParameter('id', $selected);
                        },
                    ));
                }  
            }
        );
}

JS

  $('.select2article').select2({
        ajax: {
            url: {{ path("text_ajax_load_select_option") }},
            dataType: 'json',
            type: 'GET',
            data: function (params) { 
                var queryParameters = {
                    term: params.term,
                }
                return queryParameters;
            },                
            processResults: function (data) {
                return {
                    results: $.map(data.selectOptions, function (selectOptions) {
                        return {
                            text: selectOptions.title,
                            id: selectOptions.id
                        }
                    })
                };
            }
        }
    })

So after sending the request, we get into our event listener. There we will get the selected id of the base_article property, after which we will add the query_builder parameter to the form with the selection by id from the Artikel entity

  • Related