Partage
  • Partager sur Facebook
  • Partager sur Twitter

[Symfony5] Insertion données ManyToMany en bdd

Aucunes insertions dans bdd tables association

Sujet résolu
    19 janvier 2021 à 11:49:52

    Bonjour, 

    Étant sur un projet d'application de gestion d'adhérent, je souhaite actuellement ajouter des adhérents grâce à un formulaire, mais je fais face à un problème que je n'ai pas vu en cours et où très peu de forum y répondent (ou du moins je fais les mauvaises recherches).

    J'arrive à inséré des données dans mes tables avec les relation OneToMany, mais au contraire je n'y arrive pas avec les relations ManyToMany (tables composées de deux champs avec deux clés primaires et deux clés étrangères pour être plus clair).

    Mon formulaire à été généré grâce à la console.

    Le formulaire en question :

    public function buildForm(FormBuilderInterface $builder, array $options)
        {
            $builder
                ->add('nom', TextType::class)
                ->add('prenom', TextType::class)
                ->add('date_naissance', TextType::class)
                ->add('famille', EntityType::class, array('class' => 'App\Entity\Famille', 'choice_label' => 'Libelle'))
                ->add('responsables', EntityType::class, array('class' => 'App\Entity\Responsable', 'multiple' => 'Id'))
                ->add('activites', EntityType::class, array('class' => 'App\Entity\Activites', 'multiple' => 'Id'))
                ->add('adhesion', EntityType::class, array('class' => 'App\Entity\Adhesion', 'choice_label' => 'Id'))
    
                ->add('enregistrer', SubmitType::class, array('label' => 'Ajouter'))
            ;
        }
    
        public function configureOptions(OptionsResolver $resolver)
        {
            $resolver->setDefaults([
                'data_class' => Adherent::class,
            ]);
        }

    Les entités Responsables et Activités sont en relations ManyToMany avec l'entité Adhérent, mon formulaire affiche bien les valeurs en question, mais ne veut pas me les insérer dans ma table reliant la table Responsable à adhérent (adherent_responsable), de même pour responsables.

    La fonction d'ajout ci-dessous :

    public function ajouterAdherent(Request $request)
        {         
            $adherent = new Adherent();
            $form = $this->createForm(AdherentType::class, $adherent);
            $form->handleRequest($request);
                 
            if ($form->isSubmitted() && $form->isValid()) 
            {             
                $adherent = $form->getData();
                $entityManager = $this->getDoctrine()->getManager();
                $entityManager->persist($adherent);
                $entityManager->flush();
    
                return $this->render('adherent/consulterAdherent.html.twig', ['pAdherent' => $adherent,]);
            }
            else
            {
                return $this->render('adherent/ajouterAdherent.html.twig', array('form' => $form->createView(),));
            }
        }

    Je vous remercie de votre aide !

    -
    Edité par Tomios14 19 janvier 2021 à 11:50:25

    • Partager sur Facebook
    • Partager sur Twitter
      19 janvier 2021 à 16:22:16

      Salut

      La ligne 9 du code d'ajouterAdherent() est inutile, $adherent est automatiquement "mis à jour" avec les données du formulaire du fait des lignes 4 et 5.

      J'aimerais voir le code des méthodes Activite::addAdherent et Adherent::addActivite, ainsi que les mappings des propriétés Activite#adherents et Adherent#activites. Juste cela et pas l'entier des entités, merci d'avance.

      • Partager sur Facebook
      • Partager sur Twitter
        19 janvier 2021 à 16:42:48

        Merci pour ta réponse !

        Voici la méthode dans Activité :

        public function addAdherent(Adherent $adherent): self
            {
                if (!$this->adherent->contains($adherent)) {
                    $this->adherent[] = $adherent;
                }
        
                return $this;
            }

        Et dans Adhérent :

        public function addActivite(Activites $activite): self
            {
                if (!$this->activites->contains($activite)) {
                    $this->activites[] = $activite;
                    $activite->addAdherent($this);
                }
        
                return $this;
            }

        Mapping de Activité#adherents :

        /**
             * @ORM\ManyToMany(targetEntity=Adherent::class, inversedBy="activites")
             */
            private $adherent;

        Mapping de Adherent#activites

        /**
             * @ORM\ManyToMany(targetEntity=Activites::class, mappedBy="adherent")
             */
            private $activites;

        Merci à toi !


        • Partager sur Facebook
        • Partager sur Twitter
          19 janvier 2021 à 19:35:24

          Je n'avais pas fait attention : 'multiple' => 'Id', c'est pour quoi faire ? Je te propose de reprendre la documentation de EntityType, parce que pour moi c'est pas correct.

          A part ça qui peut poser problème, je ne vois pas trop.

          -
          Edité par Ymox 21 octobre 2023 à 0:22:48

          • Partager sur Facebook
          • Partager sur Twitter
            19 janvier 2021 à 21:33:11

            J'avais essayé auparavant choice_label pour cette ligne,

             ->add('paiements', EntityType::class, array('class' => 'App\Entity\Paiement', 'choice_label' => 'Libelle'))

            mais cela me donner un message d'erreur qui était celui-ci : 

            Entity of type "Doctrine\Common\Collections\ArrayCollection" passed to the choice field must be managed. Maybe you forget to persist it in the entity manager?

            Je l'ai donc remplacé par un multiple, et cela me permettais de ne plus afficher ce message d'erreur, et afficher une liste déroulante à sélection multiple (crtl+clic) et l'insertion des données restantes. 

            En voyant ce message d'erreur j'ai donc inséré les use suivants pensant que ça aller le régler le problème

            use Doctrine\Common\Collections\ArrayCollection;
            use Doctrine\Common\Collections\Collection;

            Et donc la réponse est non, j'ai vérifié mes entités si elles étaient bien liées, pas de problème là-dessus du fait que je l'ai ai créée grâce à la console..



            -
            Edité par Tomios14 19 janvier 2021 à 21:34:31

            • Partager sur Facebook
            • Partager sur Twitter
              19 janvier 2021 à 23:13:03

              Le truc est que si 'multiple' => 'Id' fonctionne, c'est "par chance". La documentation incomprise dit que c'est un booléen qui doit être défini pour 'multiple', et non une chaîne de caractères. Heureusement pour toi, une chaîne de caractères non vide est considérée comme équivalent à un booléen true en PHP.

              J'ai une dernière cartouche à tirer avant une autre demain matin : la bonne vieille FAQ PHP qui propose diverses pistes.

              -
              Edité par Ymox 21 octobre 2023 à 0:23:07

              • Partager sur Facebook
              • Partager sur Twitter
                20 janvier 2021 à 11:00:09

                J'ai suivi les réponses de ton forum (très intéressant), je n'ai plus l'erreur précédente, mais cela ne m'affiche pas la liste des responsables (car j'ai essayé uniquement sur cette entité (qui est en *ToMany)), j'ai tout d'abord modifié mon entité Adherent en modifiant addAdherent par setAdherent : 

                public function addResponsable(Responsable $responsable): self
                    {
                        if (!$this->responsables->contains($responsable)) {
                            $this->responsables[] = $responsable;
                            $responsable->setAdherent($this);
                        }
                
                        return $this;
                    }

                Puis la fonction remove de celle-ci :

                public function removeResponsable(Responsable $responsable): self
                    {
                        if ($this->responsables->contains($responsable)) {
                        $this->responsables->removeElement($responsable);
                        // set the owning side to null (unless already changed)
                            if ($responsable->getAdherent() === $this) {
                                $thing->setAdherent(null);
                            }
                        }
                
                        // if ($this->responsables->removeElement($responsable)) {
                        //     $responsable->removeAdherent($this);
                        // }
                
                        return $this;
                    }

                Puis dans mon entité Responsable j'ai ajouté la fonction setAdherent :

                public function setAdherent(Adherent $adherent = null)
                    {
                        $this->adherent = $adherent;
                    }

                Et enfin dans mon AdherentType, j'y ai modifié add Responsables :

                 ->add('responsables', CollectionType::class, array('entry_type' => ResponsableType::class, 'by_reference' => false))

                Tout en ajoutant le

                use Symfony\Component\Form\Extension\Core\Type\CollectionType;

                Pour info ResponsableType est déjà existant, donc je ne pense pas que cela pose un problème, voilà ce que m'affiche le formulaire maintenant :



                Nota : Activités et encore en multiple, c'est pour cela que ça m'affiche toutes les activités.



                -
                Edité par Tomios14 20 janvier 2021 à 11:00:53

                • Partager sur Facebook
                • Partager sur Twitter
                  20 janvier 2021 à 11:27:54

                  Pourquoi as-tu créé un setAdherent() alors que tu as une ManyToMany ?! La FAQ dit que c'est à mettre dans l'entité inverse d'une *ToOne, or tu n'as pas de ManyToOne…

                  Ou du moins le sujet parlait de problèmes avec une ManyToMany entre Adherent et Activites (tiens, une entité au nom avec un pluriel), et pas de la relation entre Activites et Responsable, qui survient uniquement depuis le message précédent…

                  -
                  Edité par Ymox 20 janvier 2021 à 11:28:10

                  • Partager sur Facebook
                  • Partager sur Twitter
                    20 janvier 2021 à 11:40:08

                    Oui, j'ai dû le faire sans réfléchir effectivement. J'ai essayé sur Responsable, du fait que cette relation est elle aussi en ManyToMany. Dans mon entité responsable :

                    /**
                     * @ORM\ManyToMany(targetEntity=Adherent::class, inversedBy="responsables")
                     */
                     private $adherent;

                    J'ai bien relu le forum, les modifications apportées m'affiche le formulaire, mais pas la liste des activités et des responsables

                    ->add('responsables', CollectionType::class, array('entry_type' => ResponsableType::class, 'by_reference' => false))
                    ->add('activites', CollectionType::class, array('entry_type' => ActivitesType::class, 'by_reference' => false))

                    Mais après consultation de la doc Symfony, je pense avoir trouvé grâce à ta direction (merci !), j'ai donc dans Mon AdherentType modifier la ligne :

                    ->add($builder->create('activites', FormType::class, ['by_reference' => false])
                    ->add('activites', EntityType::class, array('class' => 'App\Entity\Activites', 'choice_label' => 'Id')))

                    Et ça m'insére enfin ma ligne dans ma bdd :)

                    Il ne me reste plus qu'à trouvé un moyen pour permettre la selection multiple, du fait que ça m'affiche seulement une liste déroulante





                    -
                    Edité par Tomios14 20 janvier 2021 à 11:52:10

                    • Partager sur Facebook
                    • Partager sur Twitter
                      20 janvier 2021 à 11:56:49

                      CollectionType ne va pas t'afficher les entités existantes, pour ça c'est bien EntityType. Maintenant, si c'est pour avoir de quoi saisir une nouvelle entité, il faut bien se rendre compte qu'une collection, ça se commence à un moment, avant cela elle est vide. Le tutoriel sur ce site explique qu'il faut du JavaScript afin d'ajouter des éléments dans la collection. Par contre, un élément d'une collection est très souvent propre à ce à quoi on le lie, donc pour une ManyToMany, CollectionType ne me paraît pas vraiment adapté.

                      Donc ta ligne ->add($builder->create('activites', FormType::class, ['by_reference' => false]) n'est pas vraiment la bonne manière de faire (d'ailleurs, je ne sais même pas comment c'est possible que ça fonctionne avec le type générique FormType), mais je crois qu'il vaudrait mieux que tu expliques ce que tu cherches à faire que de simplement expliquer ton problème.

                      -
                      Edité par Ymox 21 octobre 2023 à 0:25:04

                      • Partager sur Facebook
                      • Partager sur Twitter
                        20 janvier 2021 à 12:02:17

                        C'est vrai que je n'ai pas expliqué ce que je cherche à faire :

                        Je cherche à créer un adhérent, en lui attribuant une activité (déjà existante dans la bdd) et lui attribuer un responsable (lui aussi déjà existant dans la bdd).

                        Donc je veux que mon formulaire affiche toutes les activités existantes, pareil pour les responsables, et qu'ensuite on puisse lui attribuer plusieurs activités et plusieurs responsables, grâce notamment avec une liste déroulante à sélection multiple.

                        Et donc par la suite, l'insertion dans les tables associatives entre adhérent et responsable, et adhérent et activités.

                        -
                        Edité par Tomios14 20 janvier 2021 à 12:04:00

                        • Partager sur Facebook
                        • Partager sur Twitter
                          20 janvier 2021 à 12:20:52

                          Oublie les tables un moment, tu travailles avec Doctrine et tu manipules des objets qui ont des relations entre eux.

                          Pour ce que tu m'indiques, c'est bien EntityType pour les deux champs (avec multiple à true et non une chaîne de caractères). La chose qui peut éventuellement aider, c'est d'ajouter 'by_reference' à false.

                          Une autre possibilité à tenter (à la place de la proposition ci-dessus) serait d'inverser mappedBy et inversedBy dans les relations.

                          • Partager sur Facebook
                          • Partager sur Twitter
                            20 janvier 2021 à 12:37:41

                            Effectivement, c'est ce que je cherche, j'ai donc suivi tes conseils avec la méthode suivante :

                            ->add('responsables', EntityType::class, array('class' => 'App\Entity\Responsable', 'multiple' => true, 'by_reference' => false))

                            J'ai bien une sélection multiple et un ajout en bdd effectif. Cette méthode est vraiment plus adaptée, je te remercie de ton aide et d'avoir pris le temps !

                            • Partager sur Facebook
                            • Partager sur Twitter

                            [Symfony5] Insertion données ManyToMany en bdd

                            × 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