Partage
  • Partager sur Facebook
  • Partager sur Twitter

Symfony - Formulaire - Champ mi-libre

Sujet résolu
    19 septembre 2018 à 10:28:51

    Bonjour à vous,

    Je débute mon apprentissage sur le framework Symfony et j'ai actuellement un projet à réaliser dans le cadre d'un stage.

    Je vous explique rapidement le contexte qui vous intéresse.

    Dans un formulaire, à terme, j'aurai des champs EntityType dans lequel sera proposé un certain nombre de choix selon les entités stockées dans la base de données.

    Cependant, il est possible que parmi les choix proposés, celui que souhaite l'utilisateur ne soit pas proposé. Ce que j'aurais aimé proposer à l'utilisateur c'est un lien "Mon [entité] ne se trouve pas dans la liste", qui redirigera à un formulaire "tout bête" dans une nouvelle fenêtre qui lui permet d'ajouter dans la base de donnée son choix qui n'a pas trouvé (ce choix devra être validé par le modérateur).

    Les champs seront (pour l'instant) commun à chaque entités, il y aura un champ "Nom" et un champ "Abréviation".

    On pourrait faire une route pour chaque formulaire mais je ne trouve pas ça évolutif, donc je me suis dis que je pourrais peut-être créer qu'une route pour toutes les entités. Voici la route que j'aimerais utiliser (avec la syntaxe en annotation) :

        /**
         * @Route("/entity/{entityName}/add",
         * name="add_entity",
         * requirements={"entityName"="\w+"})
         */
        public function addEntity($entityName) {
            $form = $this->createForm(/*Il faut ici entrer le bon formulaire*/::class);
    
            return $this->render('form/defaultForm.html.twig', array(
                'form' => $form->createView(),
            ));
        }

    Donc la problématique technique est "Comment faire pour retrouver le bon formulaire à partir du nom de l'entité ?". En soit, je pense que ça doit être réalisable en faisant une sorte de DP Factory (j'ai des connaissance en POO avec Java) mais peut-être que Symfony nous simplifie la vie (encore une fois) en nous proposant sa solution ?

    Voilà, en vous souhaitant une excellente journée et en espérant avoir été clair !

    Merci d'avance.

    -
    Edité par BenjaminDst 3 octobre 2018 à 11:26:59

    • Partager sur Facebook
    • Partager sur Twitter
      19 septembre 2018 à 11:27:58

      Hello,

      C'est une simple proposition, mais tu pourrais utiliser isMethod() sur ta requête pour dans un premier temps vérifier si le formulaire a été submit. Si oui, tu vérifie si le champ "Mon entité n'est pas dans la liste" est coché/sélectionné, et si c'est le cas, tu construis ton nouveau formulaire et tu renvoie la vue ?

      • Partager sur Facebook
      • Partager sur Twitter
      Celui qui croit tout savoir ne sait rien
        19 septembre 2018 à 11:53:58

        Salut Algorun,

        Pourquoi faire simple quand on peut faire compliqué ? :D

        En effet, je vois un peu le workflow:

        Si le formulaire n'a pas été submit, j'affiche le formulaire de base. Sinon, je vérifie que des champs ont été marqué comme "Absent de la liste" (parce que je pourrais simplement mettre un choix "Absent de la liste" je pense). Dans ce cas j'affiche un nouveau formulaire avec un champ libre pour chaque champs. Dans le cas où aucun champ n'a été marqué comme "Absent de la liste", le formulaire est vérifié et persisté.

        J'aime vraiment l'idée. Mais admettons que nous ayons 4 champs qui sont marqués comme "Absent de la liste", nous serons obligé de faire un gros if {} else if {} else if {} else {} pour chaque champs ? C'est une solution à chaud, mais en réfléchissant plus POO, il doit y avoir une solution pour éviter le if {}.

        En tout cas, je vais garder ton idée ! Merci beaucoup.

        • Partager sur Facebook
        • Partager sur Twitter
          19 septembre 2018 à 12:18:43

          Effectivement il y a d'autres solutions, le tout est de trouver lesquelles existent et lesquelles sont adaptées :p

          Pour le soucis que tu soulève, effectivement, cette méthode devient vite limitée, elle fonctionnera pour 1 ou 2 champs mais au delà il faut aborder le problème autrement.

          En ce cas, pourquoi ne pas créer un formulaire contenant tes éléments PLUS des FormType correspondants à tes entités "possiblement à créer" et null par défaut.

          Ainsi, côté vue, tu affichage chaque sous-formulaire dans sa div bien à lui, qui elle est "hidden". Puis le reste se fait en JS : si l'utilisateur choisis "Absent de la liste", tu déclenche l'affichage du formulaire correspondant (juste en dessous ?) et vice-versa.

          Ensuite à la soumission, tu dois simplement vérifier que si l'utilisateur à choisi "Absent de la liste", alors il faut impérativement valider le formulaire correspondant. Au lieu d'avoir plusieurs if...else if...else imbriqués, tu te retrouve ainsi avec plusieurs if qui se suivent mais sans "impact" sur la reste (plus besoin de redirection, tout est validé sur le même formulaire, donc possibilité de tout réafficher si erreur).

          Je trouve cette solution un peu tricky cependant, pas totalement optimisée, mais ça peut être une piste également ^^

          • Partager sur Facebook
          • Partager sur Twitter
          Celui qui croit tout savoir ne sait rien
            19 septembre 2018 à 12:28:13

            En effet, c'est la première solution à laquelle j'avais pensé. La présence du JS et les champs qui s'affiche directement rendrait aussi la chose plus ergonomique, on ne passerait pas par deux formulaires !

            Je vais y méditer, merci ! Je reviens par là dans l'après-midi ou demain !

            • Partager sur Facebook
            • Partager sur Twitter
              3 octobre 2018 à 11:21:28

              Mieux vaut tard que jamais, je me suis remis sur ce sujet et j'ai choisis la solution de faire apparaître deux autre champs en JS. Je vous montre l'aperçu du côté client.

              En utilisation classique :

              Utilisation classique

              Dans le cas où la donnée voulue n'est pas connue par la base :

              Nouvelle donnée

              C'est niveau Symfony où ça casse, en effet, il ne comprend pas la présence de ces champs dans le formulaire (ce que je comprends). Je vous montre un aperçu du code PHP.

              Le formulaire de sous-requête :

              <?php
              
              namespace App\Form\ServerRequest;
              
              [...]
              
              class ServerSubrequestType extends AbstractType {
              
                  public function buildForm(FormBuilderInterface $builder, array $options) {
              
                      $builder
                          [...]
                          ->add('serverFunction', ServerFunctionType::class, array(
                              'required' => true,
                              'label' => 'Fonction',
                          ))
                          [...];
                  }
              
                  public function getBlockPrefix() {
              
                      return 'ServerSubrequestType';
                  }
              
                  public function configureOptions(OptionsResolver $resolver) {
                
                    $resolver->setDefaults(array(
                      'data_class' => 'App\Entity\ServerSubrequest',
                      'allow_extra_fields' => true,
                    ));
                  }
              }

              Le formulaire de l'entité concernée (ServerFunctionType) :

              <?php
              
              namespace App\Form;
              
              [...]
              
              class ServerFunctionType extends AbstractType {
              
                  public function buildForm(FormBuilderInterface $builder, array $options) {
              
                      $builder
                          ->add('serverFunction', EntityType::class, array(
                              'required' => false,
                              'label' => 'Fonction',
                              'class' => ServerFunction::class,
                          ))
                          ->add('isStored', CheckboxType::class, array(
                              'required' => false,
                              'label' => ' '
                          ))
                          ->add('abbreviation', TextType::class, array(
                              'required' => false,
                              'label' => 'Abréviation',
                          ))
                          ->add('label', TextType::class, array(
                              'required' => false,
                              'label' => 'Fonction',
                          ));
                  }
              
                  public function getBlockPrefix() {
                      return 'ServerFunctionType';
                  }
              
                  public function configureOptions(OptionsResolver $resolver) {
                
                    $resolver->setDefaults(array(
                      'data_class' => 'App\Entity\ServerProcessingChain'
                    ));
                  }
              }


              Et l'erreur renvoyée par Symfony :


              Neither the property "serverPlatform"
              nor one of the methods "getServerPlatform()", "serverPlatform()",
              "isServerPlatform()", "hasServerPlatform()", "__get()"
              exist and have public access in class "App\Entity\ServerPlatform".

              Je comprend bien le soucis que Symfony rencontre, mais où dois-je déclarer les champs si ce n'est dans ServerPlatformType ? Sachant que dans ServerSubrequestType ça ne marche naturellement pas non plus. Je me demande si je ne devrais pas, carrément, faire de ces formulaires des formulaires à part que j'agencerais comme voulue dans la vue afin que ça paraisse comme un seul..

              En attente de votre aide, je vous souhaite une bonne fin de matinée et une bonne après midi.

              -
              Edité par BenjaminDst 3 octobre 2018 à 11:22:30

              • Partager sur Facebook
              • Partager sur Twitter
                3 octobre 2018 à 11:54:15

                Salut !

                Un autre membre avait un besoin similaire, je lui avais fait cette proposition.

                Sinon, je sais que j'ai aussi eu discuté de DataTransformers qui regarderaient ce qu'il y a dans le champ. S'il y a un simple ID, on récupère l'objet choisi. Si on a d'autres données, alors on créé l'objet. Un petit instant que je retrouve la discussion…

                Edit

                Voilà.

                -
                Edité par Ymox 3 octobre 2018 à 11:56:46

                • Partager sur Facebook
                • Partager sur Twitter
                  3 octobre 2018 à 12:15:18

                  Salut Ymox,

                  Merci pour ta réponse.

                  Je trouve le DataTransformers pas inintéressant, en effet, je pourrais m'en servir pour d'autre champs, mais laisser le champ que je montre en champ texte me dérange un peu. D'autant plus qu'il y deux informations à rentrer : l'abréviation et le libellé.. Peut-être aurait tu autre chose à me proposer ?

                  Si je met les champs isStored, abbreviation et label en 'mapped' => false, ces champs seront ignorés et donc l'erreur que j'ai n'apparaîtra pas ? De ce fait, je pourrais établir la condition suivante : "Si la checkbox est coché, je crée une nouvelle fonction avec en abréviation et en libellé les valeurs indiquées, sinon, je prend l'entité renseignée dans la liste de choix." ?

                  Je me demande si c'est propre, j'ai besoin de ton avis d’expérimenté.

                  Encore merci.

                  EDIT:

                  Dans le cas où je basculerai les dits-champs dans le ServerSubrequestType.

                  -
                  Edité par BenjaminDst 3 octobre 2018 à 12:20:45

                  • Partager sur Facebook
                  • Partager sur Twitter
                    3 octobre 2018 à 12:25:52

                    Le DataTransformer proposé dans l'autre sujet n'est qu'une piste pour la logique de transformation. Quand je parlais d'un ID ou d'autres informations dans le champ, ces autres informations peuvent être un tableau avec tout ce qui est nécessaire pour créer un objet Fonction.

                    Maintenant, je pense que tu pourrais aussi avoir besoin d'un type de formulaire personnalisé, qui ait deux champs : un pour l'EntityType, pour ce qui existe, et un FonctionType pour la création d'un nouvel élément. Ce nouveau type de champ accepte toutes les données et peut le valider (au pire tu créés les contraintes nécessaires dans un nouveau validateur), et le DataTransformer devrait avoir tout ce qu'il te faut. Un peu de JavaScript pour la gestion de l'affichage, et on devrait être bon.

                    L'avantage de cette solution sur la tienne est que là où tu en as besoin, tu n'as pas la nécessité de mettre un EntityType et un FonctionType non-mappé, tu utilises uniquement ce "CreateOrSelectType". Si tu as plusieurs champs pour une fonction, c'est plus simple.

                    -
                    Edité par Ymox 3 octobre 2018 à 12:29:34

                    • Partager sur Facebook
                    • Partager sur Twitter
                      3 octobre 2018 à 12:33:47

                      Je fais des tests et je te tiens au jus pour la solution que j'aurais utilisé.

                      Pour ce qui est du formulaire personnalisé, j'y ai pensé, d'autant plus que j'ai quatre champs similaires à celui que j'ai montré. J'aurais aimé un Type réutilisable mais je ne sais pas comment faire. Déjà que je n'étais pas sur que cette première solution marche, je me suis dit "On verra plus tard", si tu peux me faire part d'un lien pour la logique du truc, je t'en serai très reconnaissant.

                      Encore merci.

                      EDIT: Cela m'aurait aussi permis de ne faire qu'une vue au lieu de quatre quasi-identiques.

                      -
                      Edité par BenjaminDst 3 octobre 2018 à 12:35:59

                      • Partager sur Facebook
                      • Partager sur Twitter
                        3 octobre 2018 à 13:39:00

                        Voici, brut de cerveau et d'éditeur de texte, une piste pour ce nouveau type.

                        <?php
                        
                        use Symfony\Bridge\Doctrine\Form\Type\EntityType;
                        
                        class CreateOrSelectType extends EntityType
                        {
                        	public function buildForm(FormBuilderInterface $builder, array $options)
                        	{
                        		$builder
                        			->add('existing_value', EntityType::class, array_diff_key($options, array('new_value_type', 'new_value_options'))
                        			->add('new_value', $options['new_value_type'], $options['new_value_options'])
                        		;
                        	}
                        
                            /**
                             * {@inheritdoc}
                             */
                            public function configureOptions(OptionsResolver $resolver)
                            {
                        		parent::configureOptions($resolver);
                        
                                $resolver
                        			->setDefaults(array(
                        				'new_value_options' => array(),
                        			))
                        			->setRequired(array(
                        				'new_value_type' => false,
                        			))
                        		;
                            }
                        }

                        Le type étend EntityType et prend les mêmes options, qui seront passées à l'EntityType réellement utilisé de manière interne pour les valeurs existantes — reste encore à voir comment y ajouter l'option pour dire qu'on va créer une nouvelle valeur.
                        Sinon, il y a un paramètre supplémentaire obligatoire 'new_value_type' qui est le type du second champ, ce doit être une chaîne représentant le nom complet de la classe du formulaire, donc ce qui est retourné par \Mon\TrucType::class. Il y a moyen de passer des options à ce champ pour la nouvelle valeur avec 'new_value_options'.

                        • Partager sur Facebook
                        • Partager sur Twitter
                          3 octobre 2018 à 15:01:42

                          Je crois comprendre la structure de ce formulaire personnalisé. Bien qu'il manque la checkbox. Le plus dur est bien sur d'y intégrer une "logique", qui dirait au formulaire que si la checkbox est cochée alors il doit renvoyer "new_value" et que si elle ne l'est pas alors il doit renvoyer "existing_value".

                          Je ne vois du tout où caler du if et du return dans tout ça. J'ai surement un manque de connaissance en PHP.

                          --- EDIT ---

                          J'ai poursuivi dans mon idée et j'ai réussi à avoir le résultat voulu, mais j'avoue ne pas être fier de la manière dont je l'obtiens, je pense qu'on peut faire plus "joli".

                          J'ajoute une nouvelle fonction de serveur :

                          Ajout d'une nouvelle fonction

                          L’entité est bien créée et mon serveur est bien associée à cette nouvelle fonction :

                          ça marche

                          Je vous montre le code (que j'ai du répété pour les quatre champs qui sont comme ça).

                          Le code du formulaire ServerFunctionType :

                          <?php
                          
                          namespace App\Form;
                          
                          [...]
                          
                          class ServerFunctionType extends AbstractType {
                          
                              public function buildForm(FormBuilderInterface $builder, array $options) {
                          
                                  $builder
                                      ->add('abbreviation', TextType::class, array(
                                          'required' => false,
                                          'label' => 'Abréviation',
                                      ))
                                      ->add('label', TextType::class, array(
                                          'required' => false,
                                          'label' => 'Fonction',
                                      ));
                              }
                          
                              public function getBlockPrefix() {
                                  return 'ServerFunctionType';
                              }
                          
                              public function configureOptions(OptionsResolver $resolver) {
                            
                                $resolver->setDefaults(array(
                                  'data_class' => 'App\Entity\ServerFunction'
                                ));
                              }
                          }


                          Le code de la sous-requête ServerSubrequestType :

                          <?php
                          
                          namespace App\Form\ServerRequest;
                          
                          [...]
                          
                          class ServerSubrequestType extends AbstractType {
                          
                              public function buildForm(FormBuilderInterface $builder, array $options) {
                          
                                  $builder
                                      [...]
                                      ->add('serverFunction', EntityType::class, array(
                                          'required' => false,
                                          'label' => 'Fonction',
                                          'class' => ServerFunction::class,
                                      ))
                                      ->add('serverFunctionAdded', CheckboxType::class, array(
                                          'required' => false,
                                          'label' => ' ',
                                          'mapped' => false,
                                      ))
                                      ->add('newServerFunction', ServerFunctionType::class, array(
                                          'required' => true,
                                          'label' => 'Fonction',
                                          'mapped' => false,
                                      ))
                                      [...];
                              }
                          
                              public function getBlockPrefix() {
                          
                                  return 'ServerSubrequestType';
                              }
                          
                              public function configureOptions(OptionsResolver $resolver) {
                            
                                $resolver->setDefaults(array(
                                  'data_class' => 'App\Entity\ServerSubrequest',
                                  'allow_extra_fields' => true,
                                ));
                              }
                          }

                          Le code dans le contrôleur qui permet de gérer ça :

                                      $entityManager = $this->getDoctrine()->getManager();
                                      $serverRequest = $form->getData();
                          
                                      foreach($form->get('serverSubrequests') as $serverSubrequest) {
                                          if ($serverSubrequest->get('serverFunctionAdded')->getData() === true) {
                                              $newServerFunction = $serverSubrequest->get('newServerFunction')->getData();
                                              $entityManager->persist($newServerFunction); 
                                              $serverSubrequest->getData()->setServerFunction($newServerFunction); 
                                          }
                                          [... x4 ....]
                          }


                          Je te laisse donner ton avis sur la méthode utilisée, j'aimerais bien faire quelque chose de propre à terme !

                          En tout cas, ça marche.

                          -
                          Edité par BenjaminDst 3 octobre 2018 à 15:46:34

                          • Partager sur Facebook
                          • Partager sur Twitter
                            3 octobre 2018 à 18:04:33

                            A mon avis, si tu mettais la logique que tu as dû dupliquer 4 fois dans le contrôleur dans un DataTransformer, justement, tu y gagnerais un peu. Mais ça implique de regrouper les champs serverFunction, newServerFunction et serverFunctionAdded dans un type à part, type auquel tu pourrais justement lier le DataTransformer.

                            Au final, c'est très proche de ce à quoi je pensais, simplement je n'avais pas de checkbox et la logique aurait été dans un DataTransformer  ^^

                            • Partager sur Facebook
                            • Partager sur Twitter
                              4 octobre 2018 à 8:54:35

                              Il faut vraiment que je m'intéresse de plus près au DataTransformer alors ! Je pense qu'il me sera indispensable dans peu de temps.
                              • Partager sur Facebook
                              • Partager sur Twitter
                                9 octobre 2018 à 9:01:40

                                Salut,

                                Je me suis re-penché sur le sujet du DataTransformer et donc j'aimerais bien l'utiliser.

                                J'ai besoin de ton aide Ymox, je ne comprends pas comment la fonction "reverseTransform" peut aller regarder la valeur des champs. Dans les exemples que j'ai regardé, le transformer s'applique souvent sur un seul champ, ce qui est plus simple...

                                Je continu de me documenter en attendant ton coup de pouce !

                                • Partager sur Facebook
                                • Partager sur Twitter
                                  9 octobre 2018 à 10:03:22

                                  Normalement, le DataTransformer a accès à toutes les données du formulaire. Comme tu l'appliques sur un formulaire composé de plusieurs champs, tu as accès à chacun d'eux.

                                  la méthode reverseTransform reçoit probablement un tableau avec les valeurs des trois champs (un dump() du paramètre le prouverait facilement sans attente). En fonction de chacun des trois valeurs (peut-être deux, il y a une case à cocher si je me souviens bien, et quand elle n'est pas cochée…), tu créés ou sélectionnes la fonction.

                                  • Partager sur Facebook
                                  • Partager sur Twitter
                                    9 octobre 2018 à 11:02:32

                                    En effet, en appliquant le Transformer à tout le formulaire, celui-ci reçois un array de la valeur de tous les champs dans sont reverseTransform..

                                    A partir de là, je devrais y arriver "sans problèmes". Je te tiens au jus !

                                    --- EDIT ---

                                    J'y suis parvenu pour un champ en particulier, j'essaie de faire le type qui marche pour tous en suivant ton conseil plus haut.

                                    -
                                    Edité par BenjaminDst 9 octobre 2018 à 11:15:53

                                    • Partager sur Facebook
                                    • Partager sur Twitter
                                      12 octobre 2018 à 9:43:43

                                      Salut,

                                      J'ai un soucis pour la méthode transform() du DataTransformer du coup, lors du formulaire d'édition, le champ ne s'affiche pas sur la valeur actuelle de l'objet..

                                      • Partager sur Facebook
                                      • Partager sur Twitter
                                        12 octobre 2018 à 9:57:14

                                        Tu nous montres le code du transformer ? Merci.

                                        • Partager sur Facebook
                                        • Partager sur Twitter
                                          19 octobre 2018 à 9:21:26

                                          Salut,

                                          Tout d'abord, désolé pour la dernière fois, j'ai eu des soucis dans le déroulement de mon projet et ça m'a un peu remonté, j'ai oublié les règles de politesse.

                                          Je partage alors le code du nouveau type "SearchOrCreate".

                                          SearchOrCreateType :

                                          <?php
                                          
                                          [...]
                                          
                                          use App\Form\DataTransformer\SelectOrCreateTransformer;
                                          
                                          class SelectOrCreateType extends AbstractType {
                                          
                                              private $transformer;
                                          
                                              public function __construct(SelectOrCreateTransformer $transformer) {
                                                  
                                                  $this->transformer = $transformer;
                                              }
                                          
                                              public function buildForm(FormBuilderInterface $builder, array $options) {
                                          
                                                  $builder
                                                      ->add('existing_value', EntityType::class, array(
                                                          'class' => $options['class'],
                                                          'required' => false,
                                                          'label' => false
                                                      ))
                                                      ->add('value_added', CheckboxType::class, array(
                                                          'required' => false,
                                                          'label' => false,
                                                      ))
                                                      ->add('new_value', $options['new_value_type'],array(
                                                          'required' => false,
                                                          'label' => false
                                                      ))
                                                      ->addModelTransformer($this->transformer)
                                                  ;
                                              }
                                          
                                              public function configureOptions(OptionsResolver $resolver) {
                                                  
                                                  parent::configureOptions($resolver);
                                          
                                                  $resolver
                                                      ->setRequired(array(
                                                          'new_value_type',
                                                          'class',
                                                      ))
                                                  ;
                                              }
                                          
                                              public function getBlockPrefix() {
                                          
                                                  return 'SelectOrCreateType';
                                              }
                                          }


                                          SearchOrCreateTransformer :

                                          <?php
                                          
                                          [...]
                                          
                                          class SelectOrCreateTransformer implements DataTransformerInterface {
                                          
                                              private $entityManager;
                                          
                                              public function __construct(EntityManagerInterface $entityManager) {
                                          
                                                  $this->entityManager = $entityManager;
                                              }
                                          
                                              public function transform($value) { 
                                                  
                                                  if ($value === null) {
                                                      return null;
                                                  } else {
                                                      return array(
                                                          'existing_value' => $this->entityManager->find(get_class($value), $value->getId()),
                                                      );
                                                  }
                                              }
                                          
                                              public function reverseTransform($fields) {
                                          
                                                  if ($fields['value_added']) {
                                                      return $fields['new_value'];
                                                  } else {
                                                      return $fields['existing_value'];
                                                  }
                                                  
                                                  return;
                                              }
                                          }

                                          Merci pour le coup de main !

                                          • Partager sur Facebook
                                          • Partager sur Twitter
                                            19 octobre 2018 à 11:09:09

                                            Fais-toi pas de souci, je ne vois même pas de quelle dernière fois tu parles  ^^

                                            Je pense que le souci est à la ligne 20 de ton DataTransformer.
                                            Actuellement, tu fais récupérer un objet depuis sa classe et son ID. Il me paraît très probable que $this-&gt;entityManager-&gt;find(get_class($value), $value-&gt;getId()) soit exactement la même chose que $value directement, non ?
                                            Ensuite, il faut peut-être retourner uniquement un ID et pas l'objet entier, pour la valeur existante. Donc je soupçonne que là, 'existing_value' pourrait bien ne demander que $value-&gt;getId().

                                            P.S. : pour les &gt;, voir ici

                                            -
                                            Edité par Ymox 19 octobre 2018 à 11:10:38

                                            • Partager sur Facebook
                                            • Partager sur Twitter
                                              23 octobre 2018 à 11:50:05

                                              Saluut,

                                              C'est étrange, quand je dump() mon paramètre $value, Symfony me dit que celui-ci vaut null, qu'en pense tu ? J'ai donc toujours un champ vide d'afficher :(.

                                              Merci à toi d'avoir fermé les yeux pour la dernière fois, et pour ton temps :)

                                              • Partager sur Facebook
                                              • Partager sur Twitter
                                                23 octobre 2018 à 12:57:32

                                                Mmm, est-ce que $value contient quelque chose quand tu créés une nouvelle valeur ? Je suis en train de me demander si le transformateur n'aurait pas été lié au dernier champ et non à l'entier du type…

                                                • Partager sur Facebook
                                                • Partager sur Twitter
                                                  23 octobre 2018 à 14:20:13

                                                  Oui, voici le résultat du dump de $fields :

                                                  array:3 [▼
                                                    "existing_value" => null
                                                    "value_added" => true
                                                    "new_value" => ServerFunction {#10852 ▼
                                                      -id: null
                                                      -label: "CouCou"
                                                      -abbreviation: "CC"
                                                    }
                                                  ]



                                                  • Partager sur Facebook
                                                  • Partager sur Twitter
                                                    23 octobre 2018 à 14:41:45

                                                    Tu tentes de créer un nouveau ServerRequest quand tu as null, ou tu es bien en train d'en éditer un qui a un ServerFunction ?

                                                    • Partager sur Facebook
                                                    • Partager sur Twitter
                                                      23 octobre 2018 à 14:48:30

                                                      J'ai simulé le cas où je ne trouvais pas la valeur souhaité dans la liste de choix. J'ai donc coché la checkbox, qui m'a fait apparaître le formulaire d'ajout d'une valeur que j'ai remplie en donnant les valeurs qu'on voit dans "new_value". En fait je prend "existing_value" quand la checkbox n'est pas cochée et "new_value" quand elle l'est.

                                                      --- EDIT ---

                                                      Oups, je n'avais pas compris, quand j'ai null lors du dump dans le transform(), c'est lors de l'édition d'un serveur qui possède ce champ "SearchToCreate"

                                                      -
                                                      Edité par BenjaminDst 23 octobre 2018 à 14:50:21

                                                      • Partager sur Facebook
                                                      • Partager sur Twitter
                                                        23 octobre 2018 à 14:55:22

                                                        Oui, mais est-ce que ce serveur possède bien une fonction ?

                                                        • Partager sur Facebook
                                                        • Partager sur Twitter
                                                          23 octobre 2018 à 16:22:50

                                                          Houla en effet, je viens de me rendre compte de ça :

                                                          ServerCreationRequest {#7506 ▼
                                                            #serverRequestContext: ServerRequestContext {#6364 ▶}
                                                            -requestedServer: RequestedServer {#7732 ▼
                                                              +__isInitialized__: false
                                                              -serverRequest: ServerCreationRequest {#7506}
                                                              -id: 1
                                                              -platform: null
                                                              -processingChain: null
                                                              -customer: null
                                                              -function: null
                                                              -nature: null
                                                              -ipAddress: null
                                                              -lifetime: null
                                                              -technicalCharacteristics: null
                                                              -backuped: null
                                                              -duplicated: null
                                                               …2
                                                            }
                                                            -id: 1
                                                            -requestedDeliveryDate: DateTime @1540684800 {#7502 ▶}
                                                            -estimate: "DEVIS"
                                                          }

                                                          Mais pourtant, en regardant dans la base de données, tout va bien ! :euh:

                                                          • Partager sur Facebook
                                                          • Partager sur Twitter
                                                            23 octobre 2018 à 17:47:58

                                                            C'est juste un "proxy" temporaire, en témoigne l'ID qui est renseigné et la propriété __isInitialized__ à false. Rien de préoccupant à ce niveau.

                                                            Ton dump($value), tu le fais où exactement ?

                                                            Et je vais finir par me demander si tu n'aurais pas une petite coquille dans ton formulaire où tu utilises ton SelectOrCreateType.

                                                            Edit

                                                            Avec ton code un peu adapté, j'arrive à quelque chose de fonctionnel.

                                                            Soient deux entités One(ParentTest)ToMany(ChildTest) avec uniquement un champ nom et la relation.

                                                            // src/Form/ParentTestType.php
                                                            namespace App\Form;
                                                            
                                                            use App\Entity\ParentTest;
                                                            use Symfony\Component\Form\AbstractType;
                                                            use Symfony\Component\Form\FormBuilderInterface;
                                                            use Symfony\Component\OptionsResolver\OptionsResolver;
                                                            
                                                            class ParentTestType extends AbstractType
                                                            {
                                                                public function buildForm(FormBuilderInterface $builder, array $options)
                                                                {
                                                                    $builder
                                                                        ->add('name')
                                                                        ->add('childTests', \Symfony\Component\Form\Extension\Core\Type\CollectionType::class, array(
                                                                            'entry_type' => \App\Form\Type\CreateOrSelectType::class,
                                                                            'entry_options' => array(
                                                                                'class' => \App\Entity\ChildTest::class,
                                                                                'new_value_type' => ChildTestType::class,
                                                                                'choice_label' => 'name',
                                                                            ),
                                                                            'by_reference' => false,
                                                                        ))
                                                                    ;
                                                                }
                                                            
                                                                public function configureOptions(OptionsResolver $resolver)
                                                                {
                                                                    $resolver->setDefaults([
                                                                        'data_class' => ParentTest::class,
                                                                    ]);
                                                                }
                                                            }
                                                            
                                                            // src/Form/Type/CreateOrSelectType.php
                                                            namespace App\Form\Type;
                                                            
                                                            use Symfony\Component\Form\AbstractType;
                                                            use Symfony\Component\Form\FormBuilderInterface;
                                                            use Symfony\Component\OptionsResolver\OptionsResolver;
                                                            
                                                            class CreateOrSelectType extends AbstractType
                                                            {    
                                                                private $transformer;
                                                                
                                                                public function __construct(\App\Form\Transformer\CreateOrSelectTransformer $transformer)
                                                                {    
                                                                    $this->transformer = $transformer;
                                                                }
                                                                
                                                                public function buildForm(FormBuilderInterface $builder, array $options)
                                                                {
                                                                    $builder
                                                                        ->add('existing_value', \Symfony\Bridge\Doctrine\Form\Type\EntityType::class, array(
                                                                            'class' => $options['class'],
                                                                            'choice_label' => $options['choice_label'],
                                                                            'required' => false,
                                                                            'label' => false
                                                                        ))
                                                                        ->add('value_added', \Symfony\Component\Form\Extension\Core\Type\CheckboxType::class, array(
                                                                            'required' => false,
                                                                            'label' => false,
                                                                        ))
                                                                        ->add('new_value', $options['new_value_type'], array(
                                                                            'required' => false,
                                                                            'label' => false
                                                                        ))
                                                                        ->addModelTransformer($this->transformer)
                                                                    ;
                                                                }
                                                                
                                                                public function configureOptions(OptionsResolver $resolver)
                                                                {    
                                                                    $resolver
                                                                        ->setRequired(array(
                                                                            'class',
                                                                            'new_value_type',
                                                                        ))
                                                                        ->setDefaults(array(
                                                                            'choice_label' => null,
                                                                        ))
                                                                    ;
                                                                }
                                                            }
                                                            
                                                            // src/Form/Transformer/CreateOrSelectTransformer.php
                                                            namespace App\Form\Transformer;
                                                            
                                                            use Symfony\Component\Form\DataTransformerInterface;
                                                            
                                                            class CreateOrSelectTransformer implements DataTransformerInterface
                                                            {
                                                                public function transform($value)
                                                                {
                                                                    $result = array(
                                                                        'new_value' => null,
                                                                        'existing_value' => null,
                                                                        'value_added' => false,
                                                                    );
                                                                    if ($value && $value->getId() === null) {
                                                                        $result['new_value'] = $value;
                                                                        $result['value_added'] = true;
                                                                    } else if ($value && $value->getId() != null) {
                                                                        $result['existing_value'] = $value;
                                                                    }
                                                                    
                                                                    return $result;
                                                                }
                                                            
                                                                public function reverseTransform($fields)
                                                                {
                                                                    $result = null;
                                                                    if ($fields['value_added'] && $fields['new_value']) {
                                                                        $result = $fields['new_value'];
                                                                    } else {
                                                                        $result = $fields['existing_value'];
                                                                    }
                                                            
                                                                    return $result;
                                                                }
                                                            }
                                                            

                                                            -
                                                            Edité par Ymox 24 octobre 2018 à 12:36:30

                                                            • Partager sur Facebook
                                                            • Partager sur Twitter
                                                              24 octobre 2018 à 16:02:27

                                                              Je viens de voir ton edit, je regarde ça de suite, merci.

                                                              --- EDIT ---

                                                              Je ne comprends pas, le dump de mon result est bien :

                                                              array:3 [▼
                                                                "existing_value" => Customer {#11571 ▼
                                                                  +__isInitialized__: true
                                                                  -id: 1
                                                                  -label: "Client"
                                                                  -abbreviation: "CL"
                                                                   …2
                                                                }
                                                                "value_added" => null
                                                                "new_value" => null
                                                              ]

                                                              Mais rien y fait, cette fichu valeur ne veut pas s'afficher.. Où le problème peut-il bien être, c'est étrange..

                                                              Je te renvoie mon code si jamais tu vois quelque chose o_O

                                                              <?php
                                                              
                                                              namespace App\Form\DataTransformer;
                                                              
                                                              use Doctrine\ORM\EntityManagerInterface;
                                                              use Symfony\Component\Form\DataTransformerInterface;
                                                              use Symfony\Component\Form\Exception\TransformationFailedException;
                                                              use App\Entity\Server;
                                                              
                                                              class SelectOrCreateTransformer implements DataTransformerInterface {
                                                              
                                                                  private $entityManager;
                                                              
                                                                  public function __construct(EntityManagerInterface $entityManager)
                                                                  {
                                                                      $this->entityManager = $entityManager;
                                                                  }
                                                              
                                                                  public function transform($value)
                                                                  {
                                                                      $result = array(
                                                                          'existing_value' => null,
                                                                          'value_added' => null,
                                                                          'new_value' => null,
                                                                      );
                                                              
                                                                      if ($value && $value->getId() === null) {
                                                                          $result['value_added'] = true;
                                                                          $result['new_value'] = $value;
                                                                      } else if ($value && $value->getId() != null) {
                                                                          $result['existing_value'] = $value;
                                                                      }
                                                              
                                                                      dump($result);
                                                              
                                                                      return $result;
                                                                  }
                                                              
                                                                  public function reverseTransform($fields)
                                                                  {
                                                                      if ($fields['value_added']) {
                                                                          return $fields['new_value'];
                                                                      } else {
                                                                          return $fields['existing_value'];
                                                                      }
                                                                      
                                                                      return;
                                                                  }
                                                              }
                                                              <?php
                                                              
                                                              namespace App\Form\Type;
                                                              
                                                              use Symfony\Component\Form\AbstractType;
                                                              use Symfony\Component\Form\FormBuilderInterface;
                                                              use Symfony\Component\OptionsResolver\OptionsResolver;
                                                              use Symfony\Bridge\Doctrine\Form\Type\EntityType;
                                                              use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
                                                              
                                                              use App\Form\DataTransformer\SelectOrCreateTransformer;
                                                              
                                                              class SelectOrCreateType extends AbstractType {
                                                              
                                                                  private $transformer;
                                                              
                                                                  public function __construct(SelectOrCreateTransformer $transformer) {
                                                                      
                                                                      $this->transformer = $transformer;
                                                                  }
                                                              
                                                                  public function buildForm(FormBuilderInterface $builder, array $options) {
                                                              
                                                                      $builder
                                                                          ->add('existing_value', EntityType::class, array(
                                                                              'class' => $options['class'],
                                                                              'required' => false,
                                                                              'label' => false,
                                                                          ))
                                                                          ->add('value_added', CheckboxType::class, array(
                                                                              'required' => false,
                                                                              'label' => false,
                                                                          ))
                                                                          ->add('new_value', $options['new_value_type'],array(
                                                                              'required' => false,
                                                                              'label' => false
                                                                          ))
                                                                          ->addModelTransformer($this->transformer)
                                                                      ;
                                                                  }
                                                              
                                                                  public function configureOptions(OptionsResolver $resolver) {
                                                                      
                                                                      parent::configureOptions($resolver);
                                                              
                                                                      $resolver
                                                                          ->setRequired(array(
                                                                              'new_value_type',
                                                                              'class',
                                                                          ))
                                                                      ;
                                                                  }
                                                              
                                                                  public function getBlockPrefix() {
                                                              
                                                                      return 'SelectOrCreateType';
                                                                  }
                                                              }





                                                              -
                                                              Edité par BenjaminDst 24 octobre 2018 à 16:19:25

                                                              • Partager sur Facebook
                                                              • Partager sur Twitter

                                                              Symfony - Formulaire - Champ mi-libre

                                                              × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                                                              × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                                                              • Editeur
                                                              • Markdown