Symfony3: select type field populated with an array of objects

I have a Product entity. My product can have several names in different languages. Name in French, name in English, etc. I do not want to use automatic translation.

The user will have to write down the names in the product form and select the appropriate language. He can add as many names as he wants thanks to the Add button.

All languages ​​are created by admin (in a different form). Thus, Language also an entity that has a name (for example: English) and a code (for example: EN).

I created an Entity ProductName that has a name and language (which correspond to what the user writes in the form of the product).

In this case, I do not need to associate the Entity ProductName with the Entity Language . I just need a language code. So, in my ProductName object, I have this property:

 /** * @ORM\Column(name="Language_Code", type="string", length=2) */ private $language; 

In my product form (ProductType) there is a CollectionType field to add multiple names.

 // Form/ProductType.php ->add('infos', CollectionType::class, array( 'entry_type' => ProductInfosType::class, 'allow_add' => true, 'allow_delete' => true, 'prototype' => true, 'label' => false, 'mapped' => false )) 

And the ProductInfosType form has 2 fields:

 // Form/ProductInfosType.php ->add('name', TextType::class, array( 'attr' => array('size' => 40) )) ->add('language', EntityType::class, array( 'placeholder' => '', 'class' => 'AppBundle:Language', 'choice_label' => 'code', 'attr' => array('class' => 'lang'), 'query_builder' => function (EntityRepository $er) { return $er->createQueryBuilder('l')->orderBy('l.code', 'ASC'); } )) 

So, when I go to my form page, I have a block that contains an input text field (Name) and a selection field (language). The select field is as follows:

 <select id="product_infos_0_language" required="required" name="product[infos][0][language]"> <option value=""></option> <option value="DE">DE</option> <option value="EN">EN</option> <option value="ES">ES</option> <option selected="selected" value="FR">FR</option> </select> 

At this point, everything is working well. I created an add button so that the user can add other names, etc.

But when I submit the form, when I validate the form data in my ProductController, I noticed that this does not match what I want to store in the database.

 print_r($form->get('infos')->getData()); // returns : Array ( [0] => AppBundle\Entity\ProductName Object ( [language:AppBundle\Entity\ProductName:private] => AppBundle\Entity\Language Object ( [code:AppBundle\Entity\Language:private] => FR [name:AppBundle\Entity\Language:private] => Français ) [name:AppBundle\Entity\ProductName:private] => Ceinture lombaire LombaSkin ) ) 

I would like to:

 Array ( [0] => AppBundle\Entity\ProductName Object ( [language:AppBundle\Entity\ProductName:private] => FR [name:AppBundle\Entity\ProductName:private] => Ceinture lombaire LombaSkin ) ) 

I do not want the language object, but directly indicated the language code !

That's why I think I should not use EntityField in the form of ProductNameType, but ChoiceType .

How to load all languages ​​stored in db in the select box? I hope this explanation is more clear :-)

+6
source share
2 answers

I found a solution thanks to this post: Passing data to buildForm () in Symfony 2.8 / 3.0

ProductController.php : pass user data as an option in the createForm() method.

 // ... // build the form $em = $this->getDoctrine()->getManager(); $product = new Product(); $languages = $em->getRepository('AppBundle:Language')->findAllOrderedByCode(); $form = $this->createForm(ProductType::class, $product, array( 'languages' => $languages )); 

ProductType form : passing user data in resolver parameter

 public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults(array( 'data_class' => 'AppBundle\Entity\Product', 'languages' => null )); } 

Then, in the buildForm() function, add the entry_options parameter to the CollectionType field:

 $builder->add('infos', CollectionType::class, array( 'entry_type' => ProductInfosType::class, 'entry_options' => array('languages' => $options['languages']), 'allow_add' => true, 'allow_delete' => true, 'prototype' => true, 'label' => false, 'by_reference' => false )); 

ProductInfosType form : transfer user data in the parameter converter (exactly the same as in ProductForm)

 public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults(array( 'data_class' => 'AppBundle\Entity\ProductName', 'languages' => null )); } 

You now have two alternatives: either you want your form to return objects or simple strings.

In my example, I just need a language code (e.g. FR, EN, etc.).

Case 1: only returns the language code when submitting the form:

 // Form/ProductInfosType.php // ... // Convert array of objects in an array of strings $choices = array(); foreach ($options['languages'] as $lang) { $code = $lang->getCode(); $choices[$code] = $code; } $builder->add('language', ChoiceType::class, array( 'placeholder' => '', 'choices' => $choices )); // returns : Array ( [0] => AppBundle\Entity\ProductName Object ( [name:AppBundle\Entity\ProductName:private] => Ceinture lombaire LombaSkin [language:AppBundle\Entity\ProductName:private] => FR ) ) 

Case 2: return the language object when submitting the form:

 // Form/ProductInfosType.php // ... $builder->add('language', ChoiceType::class, array( 'placeholder' => '', 'choices' => $options['languages'], 'choice_label' => 'code', 'choice_value' => 'code' )); // returns : Array ( [0] => AppBundle\Entity\ProductName Object ( [name:AppBundle\Entity\ProductName:private] => Ceinture lombaire LombaSkin [language:AppBundle\Entity\ProductName:private] => AppBundle\Entity\Language Object ( [code:AppBundle\Entity\Language:private] => FR [name:AppBundle\Entity\Language:private] => Français ) ) ) 

With this solution, we do not need to create our form as a service in order to pass the object manager as an argument. Everything is controlled in the parameters of the controller and form.

+6
source

You must use choice_value for EntityType.

 'choice_value' => function ($language) { return $language->getCode(); }, 

Strike>

EDIT:. After reading your edit, you're really right, don't use EntityType , but ChoiceType . To populate choices , I think you should enter a LanguageRepository inside your Form using DependencyInjection, and then create a query in your repository to retrieve all the language codes.

+1
source

All Articles