Partage
  • Partager sur Facebook
  • Partager sur Twitter

[Symfony 3.3] Relation Many to Many et form

    17 juillet 2019 à 10:07:48

    Bonjour,

    Je vous écris car depuis maintenant deux semaines je bloque sur un problème de relation Many to Many dans un formulaire.

    Alors voilà, j'ai deux entités liées par la relation Many to Many

    Voici ma première entité

    <?php
    
    namespace FilesBundle\Entity;
    
    /**
     * Category
     */
    class Category
    {
        /**
         * @var integer
         */
        private $id;
    
        /**
         * @var string
         */
        private $libelle;
    
        /**
         * @var \Doctrine\Common\Collections\Collection
         */
        private $files;
    
        /**
         * Constructor
         */
        public function __construct()
        {
            $this->files = new \Doctrine\Common\Collections\ArrayCollection();
        }
    
        /**
         * Get id
         *
         * @return integer
         */
        public function getId()
        {
            return $this->id;
        }
    
        /**
         * Set libelle
         *
         * @param string $libelle
         *
         * @return Category
         */
        public function setLibelle($libelle)
        {
            $this->libelle = $libelle;
    
            return $this;
        }
    
        /**
         * Get libelle
         *
         * @return string
         */
        public function getLibelle()
        {
            return $this->libelle;
        }
    
        /**
         * Add file
         *
         * @param \FilesBundle\Entity\File $file
         *
         * @return Category
         */
        public function addFile(\FilesBundle\Entity\File $file)
        {
            $this->files[] = $file;
    
            return $this;
        }
    
        /**
         * Remove file
         *
         * @param \FilesBundle\Entity\File $file
         */
        public function removeFile(\FilesBundle\Entity\File $file)
        {
            $this->files->removeElement($file);
        }
    
        /**
         * Get files
         *
         * @return \Doctrine\Common\Collections\Collection
         */
        public function getFiles()
        {
            return $this->files;
        }
    }
    
    

    Et voici ma deuxième entité qui est propriétaire de la relation

    <?php
    
    namespace FilesBundle\Entity;
    
    use Doctrine\ORM\EntityManager;
    use Doctrine\ORM\Mapping as ORM;
    use Doctrine\Common\Collections\ArrayCollection;
    
    /**
     * Folder
     */
    class Folder
    {
        /**
         * @var integer
         */
        private $id;
    
        /**
         * @var string
         */
        private $libelle;
    
        /**
         * @ORM\ManyToMany(targetEntity="FilesBundle\ENtity\Category", cascade={"persist"})
         * @ORM\JoinTable(name="cms_folders_categories")
         */
        private $categories;
    
        /**
         * Constructor
         */
        public function __construct()
        {
            $this->categories = new ArrayCollection();
        }
    
        /**
         * Get id
         *
         * @return integer
         */
        public function getId()
        {
            return $this->id;
        }
    
        /**
         * Set libelle
         *
         * @param string $libelle
         *
         * @return Folder
         */
        public function setLibelle($libelle)
        {
            $this->libelle = $libelle;
    
            return $this;
        }
    
        /**
         * Get libelle
         *
         * @return string
         */
        public function getLibelle()
        {
            return $this->libelle;
        }
    
        /**
         * Add category
         *
         * @param \FilesBundle\Entity\Category $category
         *
         * @return Folder
         */
        public function addCategory(\FilesBundle\Entity\Category $category)
        {
            $this->categories->add($category);
    
            return $this;
        }
    
        /**
         * Remove category
         *
         * @param \FilesBundle\Entity\Category $category
         */
        public function removeCategory(\FilesBundle\Entity\Category $category)
        {
            $this->categories->removeElement($category);
        }
    
        /**
         * Set categories
         *
         * @param string $categories
         *
         * @return Folder
         */
        public function setCategories($categories)
        {
            foreach($categories as $category){
                $this->addCategory($category);
            }
            return $this;
        }
    
        /**
         * Get categories
         *
         * @return \Doctrine\Common\Collections\Collection
         */
        public function getCategories()
        {
            return $this->categories;
        }
    }
    
    

    Je voulais donc pouvoir lorsque je renseigne un folder pouvoir lui associer plusieurs category, sachant qu'une category peu être dans un ou plusieurs folder (d'où la relation many to many)

    j'ai donc fait un CRUD et j'ai modifié un peu les form pour pouvoir au moment de la création d'un folder pouvoir ajouter des category (soit un soit n category)

    Voici mon FolderType.php

    <?php
    
    namespace FilesBundle\Form;
    
    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\CollectionType;
    use FilesBundle\Entity\Category;
    use FilesBundle\Form\CategoryForm;
    
    class FolderType extends AbstractType
    {
        /**
         * {@inheritdoc}
         */
        public function buildForm(FormBuilderInterface $builder, array $options)
        {
            $builder->add('libelle')->add('categories',CollectionType::class, [
                
                'entry_type' => CategoryForm::class,
                'allow_add'    => true,
                'by_reference'    => false,
            ]);
        }/**
         * {@inheritdoc}
         */
        public function configureOptions(OptionsResolver $resolver)
        {
            $resolver->setDefaults(array(
                'data_class' => 'FilesBundle\Entity\Folder'
            ));
        }
    
        /**
         * {@inheritdoc}
         */
        public function getBlockPrefix()
        {
            return 'filesbundle_folder';
        }
    
    
    }
    

    Et voici mon CategoryForm.php

    <?php
    
    namespace FilesBundle\Form;
    
    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\CollectionType;
    use FilesBundle\Entity\Category;
    use FilesBundle\Form\CategoryType;
    
    class CategoryForm extends AbstractType
    {
        /**
         * {@inheritdoc}
         */
        public function buildForm(FormBuilderInterface $builder, array $options)
        {
            $builder->add('libelle',EntityType::class, [
                
                'class' => Category::class,
                'choice_label' => 'libelle',
                'label' => 'Categorie',
            ]);
        }/**
         * {@inheritdoc}
         */
        public function configureOptions(OptionsResolver $resolver)
        {
            $resolver->setDefaults(array(
                'data_class' => 'FilesBundle\Entity\Category'
            ));
        }
    
        /**
         * {@inheritdoc}
         */
        public function getBlockPrefix()
        {
            return 'filesbundle_category';
        }
    
    
    }
    

    Donc avec un peu de JS j'arrive à une page ou peu je peu ajouter ou enlever des category dans mon formulaire de création de folder

    Le soucis viens à la validation, lorsque je valide j'obtiens cette erreur

    A new entity was found through the relationship 'FilesBundle\Entity\Folder#categories' that was not configured to cascade persist operations for entity: FilesBundle\Entity\Category@0000000029a45cbe000000000af8c468. To solve this issue: Either explicitly call EntityManager#persist() on this unknown entity or configure cascade persist this association in the mapping for example @ManyToOne(..,cascade={"persist"}). If you cannot find out which entity causes the problem implement 'FilesBundle\Entity\Category#__toString()' to get a clue.

    ET j'ai du mal à comprendre pourquoi l'erreur est sur l'entité category alors que je manipule un folder...

    Si ça peu aider voila mon FolderController

    <?php
    
    namespace FilesBundle\Controller;
    
    use FilesBundle\Entity\Folder;
    use Symfony\Bundle\FrameworkBundle\Controller\Controller;
    use Symfony\Component\HttpFoundation\Request;
    
    /**
     * Folder controller.
     *
     */
    class FolderController extends Controller
    {
        /**
         * Lists all folder entities.
         *
         */
        public function indexAction()
        {
            $em = $this->getDoctrine()->getManager();
    
            $folders = $em->getRepository('FilesBundle:Folder')->findAll();
    
            return $this->render('folder/index.html.twig', array(
                'folders' => $folders,
            ));
        }
    
        /**
         * Creates a new folder entity.
         *
         */
        public function newAction(Request $request)
        {
            //var_dump("1");
            $folder = new Folder();
            //var_dump("2");
            $form = $this->createForm('FilesBundle\Form\FolderType', $folder);
            //var_dump("3");
            $form->handleRequest($request);
            //var_dump("4");
            
            if ($form->isSubmitted() && $form->isValid()) {
               // var_dump("5");
                $em = $this->getDoctrine()->getManager();
               // var_dump("6");
                $em->persist($folder);
                /*foreach($folder->getCategories() as $category){
                    var_dump($category->getLibelle()->getLibelle());
                }
                die;*/
                $em->flush();
               // var_dump("8");
    
                return $this->redirectToRoute('folder_show', array('id' => $folder->getId()));
            }
    
            return $this->render('folder/new.html.twig', array(
                'folder' => $folder,
                'form' => $form->createView(),
            ));
        }
    
        /**
         * Finds and displays a folder entity.
         *
         */
        public function showAction(Folder $folder)
        {
            $deleteForm = $this->createDeleteForm($folder);
    
            return $this->render('folder/show.html.twig', array(
                'folder' => $folder,
                'delete_form' => $deleteForm->createView(),
            ));
        }
    
        /**
         * Displays a form to edit an existing folder entity.
         *
         */
        public function editAction(Request $request, Folder $folder)
        {
            $deleteForm = $this->createDeleteForm($folder);
            $editForm = $this->createForm('FilesBundle\Form\FolderType', $folder);
            $editForm->handleRequest($request);
    
            if ($editForm->isSubmitted() && $editForm->isValid()) {
                $this->getDoctrine()->getManager()->flush();
    
                return $this->redirectToRoute('folder_edit', array('id' => $folder->getId()));
            }
    
            return $this->render('folder/edit.html.twig', array(
                'folder' => $folder,
                'edit_form' => $editForm->createView(),
                'delete_form' => $deleteForm->createView(),
            ));
        }
    
        /**
         * Deletes a folder entity.
         *
         */
        public function deleteAction(Request $request, Folder $folder)
        {
            $form = $this->createDeleteForm($folder);
            $form->handleRequest($request);
    
            if ($form->isSubmitted() && $form->isValid()) {
                $em = $this->getDoctrine()->getManager();
                $em->remove($folder);
                $em->flush();
            }
    
            return $this->redirectToRoute('folder_index');
        }
    
        /**
         * Creates a form to delete a folder entity.
         *
         * @param Folder $folder The folder entity
         *
         * @return \Symfony\Component\Form\Form The form
         */
        private function createDeleteForm(Folder $folder)
        {
            return $this->createFormBuilder()
                ->setAction($this->generateUrl('folder_delete', array('id' => $folder->getId())))
                ->setMethod('DELETE')
                ->getForm()
            ;
        }
    }
    

    La partie commentée dans le newAction est là car je voulais vérifier que je récupérais bien mes catégory dans la variable catégories et la réponse est oui .

    Je remercie ceux qui prendrons le temps de m'aider car là je suis perdu...

    Et si vous avais besoin de plus d'info n'hésitez pas ^^







    • Partager sur Facebook
    • Partager sur Twitter
      17 juillet 2019 à 15:25:02

      Bonjour,

      il y a un truc perturbant, la casse sur "ENtity" :

      <?php
      
      // extrait de l'entité Folder
      
      /**
           * @ORM\ManyToMany(targetEntity="FilesBundle\ENtity\Category", cascade={"persist"})
           * @ORM\JoinTable(name="cms_folders_categories")
           */
          private $categories;


      Cela n'est peut-être pas un souci, mais au cas où, mieux vaut vérifier çà déjà.

      -
      Edité par symfonydu80 17 juillet 2019 à 15:25:58

      • Partager sur Facebook
      • Partager sur Twitter
        17 juillet 2019 à 15:40:58

        symfonydu80 a écrit:

        Bonjour,

        il y a un truc perturbant, la casse sur "ENtity" :

        <?php
        
        // extrait de l'entité Folder
        
        /**
             * @ORM\ManyToMany(targetEntity="FilesBundle\ENtity\Category", cascade={"persist"})
             * @ORM\JoinTable(name="cms_folders_categories")
             */
            private $categories;


        Cela n'est peut-être pas un souci, mais au cas où, mieux vaut vérifier çà déjà.

        -
        Edité par symfonydu80 il y a 10 minutes


        J'ai eu espoir quand j'ai vue ton message ^^, mais malheureusement la correction la rien changé... Arf ;)
        • Partager sur Facebook
        • Partager sur Twitter
          19 juillet 2019 à 11:08:39

          Personne n'a d'idée??? :(
          • Partager sur Facebook
          • Partager sur Twitter
            19 juillet 2019 à 13:53:55

            Ymox a écrit:

            Salut !

            En fait, la FAQ Symfony en a pour toi.


            Ho merci, je n'ai pas pensé à aller fouiller la-bas, grosse erreur de ma part.
            J'y vais de se pas

            Encore merci

            Update:

            J'ai suivis les indications de la FAQ et malheureusement ça n'a pas fonctionné.

            Du coup voila mes entités mise à jour avec les modification de la FAQ.

            <?php
            
            namespace FilesBundle\Entity;
            
            use Doctrine\ORM\EntityManager;
            use Doctrine\ORM\Mapping as ORM;
            use Doctrine\Common\Collections\ArrayCollection;
            
            /**
             * Folder
             */
            class Folder
            {
                /**
                 * @var integer
                 */
                private $id;
            
                /**
                 * @var string
                 */
                private $libelle;
            
                /**
                 * @ORM\ManyToMany(targetEntity="FilesBundle\Entity\Category", cascade={"persist"})
                 * @ORM\JoinTable(name="cms_folders_categories")
                 */
                private $categories;
            
                /**
                 * Constructor
                 */
                public function __construct()
                {
                    $this->categories = new ArrayCollection();
                }
            
                /**
                 * Get id
                 *
                 * @return integer
                 */
                public function getId()
                {
                    return $this->id;
                }
            
                /**
                 * Set libelle
                 *
                 * @param string $libelle
                 *
                 * @return Folder
                 */
                public function setLibelle($libelle)
                {
                    $this->libelle = $libelle;
            
                    return $this;
                }
            
                /**
                 * Get libelle
                 *
                 * @return string
                 */
                public function getLibelle()
                {
                    return $this->libelle;
                }
            
                /**
                 * Add category
                 *
                 * @param \FilesBundle\Entity\Category $category
                 *
                 * @return Folder
                 */
                public function addCategory(\FilesBundle\Entity\Category $category)
                {
                    var_dump("ok");
                    if(!$this->categories->contains($category)){
                        var_dump("in");
                        $this->categories[] = $category;
                        $category->setFolders($this);
                    }
                    return $this;
                }
            
                /**
                 * Remove category
                 *
                 * @param \FilesBundle\Entity\Category $category
                 */
                public function removeCategory(\FilesBundle\Entity\Category $category)
                {
                    $this->categories->removeElement($category);
                }
            
            
            
                /**
                 * Get categories
                 *
                 * @return \Doctrine\Common\Collections\Collection
                 */
                public function getCategories()
                {
                    return $this->categories;
                }
            }
            
            

            Et

            <?php
            
            namespace FilesBundle\Entity;
            
            /**
             * Category
             */
            class Category
            {
                /**
                 * @var integer
                 */
                private $id;
            
                /**
                 * @var string
                 */
                private $libelle;
            
                /**
                 * @var \Doctrine\Common\Collections\Collection
                 */
                private $files;
            
                /**
                 * @ORM\ManyToMany(targetEntity="FilesBundle\Entity\Folder", cascade={"persist"})
                 * @ORM\JoinTable(name="cms_folders_categories")
                 */
                private $folders;
            
                /**
                 * Constructor
                 */
                public function __construct()
                {
                    $this->files = new \Doctrine\Common\Collections\ArrayCollection();
                    $this->folders = new \Doctrine\Common\Collections\ArrayCollection();
                }
            
                /**
                 * Get id
                 *
                 * @return integer
                 */
                public function getId()
                {
                    return $this->id;
                }
            
                /**
                 * Set libelle
                 *
                 * @param string $libelle
                 *
                 * @return Category
                 */
                public function setLibelle($libelle)
                {
                    $this->libelle = $libelle;
            
                    return $this;
                }
            
                /**
                 * Get libelle
                 *
                 * @return string
                 */
                public function getLibelle()
                {
                    return $this->libelle;
                }
            
                /**
                 * Add file
                 *
                 * @param \FilesBundle\Entity\File $file
                 *
                 * @return Category
                 */
                public function addFile(\FilesBundle\Entity\File $file)
                {
                    $this->files[] = $file;
            
                    return $this;
                }
            
                /**
                 * Remove file
                 *
                 * @param \FilesBundle\Entity\File $file
                 */
                public function removeFile(\FilesBundle\Entity\File $file)
                {
                    $this->files->removeElement($file);
                }
            
                /**
                 * Get files
                 *
                 * @return \Doctrine\Common\Collections\Collection
                 */
                public function getFiles()
                {
                    return $this->files;
                }
            
                public function setFolders(\FilesBundle\Entity\Folder $folder)
                {
                    var_dump("in2");
                    $this->folders[] = $folder;
                }
            }
            
            




            -
            Edité par yoseil 19 juillet 2019 à 17:25:50

            • Partager sur Facebook
            • Partager sur Twitter
              19 juillet 2019 à 18:30:43

              Quand tu fournis le code, limite-toi aux propriétés impliquées (ainsi que leurs getter/setter) dans les relations qui posent problème, ça permet d'aller à l'essentiel sans encombrement souvent inutile.

              Attention, la FAQ suit un exemple qui est une OneToMany/ManyToOne, tu as une paire de ManyToMany, il te faut adapter. Il n'y a pas de méthode Category::setFolders qui tienne, mais il faut là une méthode addFolder. Je vais partir du principe que tu as aussi suivi le lien à la fin de l'entrée de FAQ précédemment citée.
              A noter qu'avec Symfony 4, une seule des deux entités possède la logique de vérification si ce qu'on lui passe est déjà dans la collection. Mais je n'espère pas me tromper en disant que de l'avoir dans les deux entités ne posera pas de problème.

              • Partager sur Facebook
              • Partager sur Twitter
                20 juillet 2019 à 17:47:06

                OK dsl pour toutes les lignes de codes inutiles.

                J'ai modifié ma méthode Category::setFolders en addFolder mais cela n'a rien changé.
                En effet j'ai bien suivi le lien à la fin de l'entrée de FAQ et pareil cela n'a rien changé.

                • Partager sur Facebook
                • Partager sur Twitter

                [Symfony 3.3] Relation Many to Many et form

                × 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