2016-05-03 2 views
6

J'ai une entité Product. Mon produit peut avoir plusieurs noms dans différentes langues. Un nom en français, un nom en anglais, etc. Je ne veux pas utiliser une traduction automatique.Symfony3: champ de type de choix rempli avec un tableau d'objets

L'utilisateur devra écrire les noms dans le formulaire de produit et sélectionner la langue correspondante. Il peut ajouter autant de noms qu'il le souhaite grâce à un bouton Ajouter.

Toutes les langues sont créées par l'utilisateur admin (sous une autre forme). Donc, Language est aussi une entité qui a un nom (ex: anglais) et un code (ex: EN).

J'ai créé l'entité ProductName qui a un nom et un langage (conforme à ce que l'utilisateur écrit dans le formulaire produit). Dans ce cas, il n'est pas nécessaire d'associer l'entité ProductName à l'entité Language. Je veux juste le code de langue. Donc, dans mon entité ProductName, j'ai cette propriété:

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

Ma forme de produit (ProductType) a un champ de CollectionType afin d'ajouter plusieurs noms.

// Form/ProductType.php 

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

Et sous forme de ProductInfosType a 2 champs:

// 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'); 
      } 
     )) 

Alors, quand je vais sur ma page formulaire, j'ai un bloc qui contient un champ de saisie de texte (Nom) et un champ de sélection (langue). Le champ de sélection est comme ceci:

<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> 

À ce stade, tout fonctionne bien. J'ai créé un bouton ajouter pour que l'utilisateur puisse ajouter d'autres noms, etc ...

Mais, quand je soumets le formulaire, quand je vérifie les données de formulaire dans mon ProductController, j'ai remarqué qu'il ne correspond pas à ce que je vouloir stocker dans la base de données.

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 
     ) 
) 

Ce que je voudrais est:

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

Je ne veux pas l'objet de la langue, mais directement le code de langue!

C'est pourquoi je pense que je ne devrais pas utiliser EntityField dans le formulaire ProductNameType mais ChoiceType.

Comment puis-je charger toutes les langues stockées dans db dans le champ de choix? J'espère que cette explication est plus compréhensible ;-)

+0

Si je comprends bien: avez-vous essayé de créer une méthode '__toString()' qui retourne la propriété 'code' de l'entité' Language'? – Delphine

+0

Non, je ne pense pas que cela m'aide. Le code de ma langue est déjà une chaîne. Ex: Nom = Anglais et Code = EN. Au lieu d'obtenir un objet Langue lorsque je soumets mon formulaire, je veux juste le code que j'ai sélectionné dans ma liste. – Felurian

+0

Je ne comprends pas vraiment ce que vous entendez par "get" après soumettre – Delphine

Répondre

6

J'ai trouvé la solution grâce à ce poste: Passing data to buildForm() in Symfony 2.8/3.0

ProductController.php: transmettre des données personnalisées en option dans la méthode createForm().

// ... 

// 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 
)); 

forme ProductType: transmettre des données personnalisées dans les options résolveur

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

Puis, dans la fonction buildForm(), ajoutez une option entry_options dans le champ CollectionType:

$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 
)); 

Formulaire ProductInfosType: transmettre des données personnalisées dans le ptions résolveur (exactement le même que dans le ProductForm)

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

Maintenant, vous avez deux possibilités: vous voulez que votre forme retourne des entités ou des chaînes simples.

Dans mon exemple, je veux juste le code de langue (comme FR, EN, etc.).

Cas 1: retour seulement le code de langue lorsque le formulaire est affiché:

// 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  
     ) 
) 

Cas n ° 2: retour Langue entité lorsque le formulaire est affiché:

// 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 
       )  
     ) 
) 

Avec cette solution, nous n'avons pas besoin de créer notre formulaire en tant que service pour passer le gestionnaire d'entités en argument. Tout est géré dans le contrôleur et les options de formulaire.

1

Vous devez utiliser choice_value de EntityType.

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

EDIT: Après avoir lu votre édition, vous êtes bien raison, ne pas utiliser EntityType mais ChoiceType. Pour remplir choices, je pense que vous devez injecter LanguageRepository dans votre formulaire en utilisant DependencyInjection, puis créer une requête dans votre référentiel pour récupérer tous les codes de langue.

+0

Non, j'ai déjà des codes comme valeurs de mes options de sélection. Cela ne change pas les données de mon formulaire. – Felurian

+0

J'ai modifié mon post pour mieux expliquer mon problème ;-) – Felurian

+0

Très bien, alors j'ai édité ma réponse aussi. – Terenoth