Partage
  • Partager sur Facebook
  • Partager sur Twitter

[Symfony] Dupliquer

Sujet résolu
15 octobre 2020 à 9:10:27

Bonjour,

j'ai une entité en relation avec d'autres entités et qui possède son CRUD. Ca fonctionne très bien.

J'ai donc sur mon appli, une page qui liste dans un tableau toutes les valeurs contenus dans la table liée à cette entité. Pour chaque ligne du tableau, j'ai une colonne "Action", avec la possibilité de Consulter / Modifier / Supprimer.

J'aimerai maintenant rajouter l'action qui permet de Dupliquer une ligne. Quand je dis dupliquer, ce n'est pas la création direct d'une nouvelle entrée en base de données copiée de la ligne choisie. J'aimerai dans un premier temp afficher le formulaire pré-rempli avec les valeurs de la ligne à dupliquer. Comme ça l'utilisateur peut modifier à sa convenance les champs qu'il souhaite et ensuite c'est lors de la validation du formulaire que la nouvelle entrée en base se fait.

Pour se faire, voici mon action pour Dupliquer :

    /**
     * @Route("/{id}/duplicate", name="hexforce_duplicate", methods={"GET","POST"})
     */
    public function duplicate(Request $request, Hexforce $hexforce): Response
    {
        // Clone de la ligne à dupliquer
        $new_hexforce = clone $hexforce;

        $form = $this->createForm(HexforceType::class, $new_hexforce);
        $form->handleRequest($request);

        // Validation du formulaire
        if ($form->isSubmitted() && $form->isValid()) {
            $entityManager = $this->getDoctrine()->getManager();

            // Ajout de certaines valeurs
            $nom_fiche = uniqid().'.pdf';
            $new_hexforce->setAuteur($this->getUser())
                         ->setCreation(new \Datetime())
                         ->setPdf($nom_fiche);
            
            // Enregistre la nouvelle entrée
            $entityManager->persist($new_hexforce);
            $this->generatePDF($new_hexforce);
            $entityManager->flush();

            $this->addFlash('success', 'The technical datasheet has been duplicated.');

            return $this->redirectToRoute('hexforce_index');
        }

        return $this->render('hexforce/edit.html.twig', [
            'hexforce' => $new_hexforce,
            'form' => $form->createView(),
        ]);
    }

Comme vous pouvez le voir, j'ai d'abord cloné l'entité à dupliquer, pour ainsi travailler sur le clone.

Résultat :

Ma nouvelle entrée en base est bien créé avec les valeurs de la ligne dupliquée si je n'ai pas touché aux champs sur le formulaire, et avec les nouvelles valeurs saisie par l'utilisateur si celui-ci à changé certains champs.

Problème :

Si sur le formulaire, je change les valeurs des champs de sélections multiples (relation manyToMany), l'entité qui a été dupliqué se voit affectée ces nouvelles valeurs également, ce que je ne comprend pas pourquoi. Alors que les autres champs (champs simple comme des input text) eux sont bien conservés.

Merci de votre aide

  • Partager sur Facebook
  • Partager sur Twitter
15 octobre 2020 à 9:31:35

Salut

Je pense qu'il faut utiliser $em->detach($hexforce) sur l'entité originale, sinon, comme tu fais un clone, pour Doctrine c'est presque le même objet, et donc les modifications faites sur l'un le sont aussi sur l'autre.

  • Partager sur Facebook
  • Partager sur Twitter
15 octobre 2020 à 9:44:54

Salut Ymox, j'espère que tu vas bien et merci pour ton aide.

"detach" est deprecated sur Symfony5, il faut utiliser "clear" à la place.

Je dois cloner l'entité à dupliquer et ensuite faire un "clear" de l'entité dupliquée ? :

        $entityManager = $this->getDoctrine()->getManager();

        // Clone de la ligne à dupliquer
        $new_hexforce = clone $hexforce;
        $entityManager->clear($hexforce);

        $form = $this->createForm(HexforceType::class, $new_hexforce);
        $form->handleRequest($request);



  • Partager sur Facebook
  • Partager sur Twitter
Anonyme
15 octobre 2020 à 9:51:15

FYI ce comportement vient du fait que clone conserve les références, et donc en l’occurrence les collections se retrouvent partagées entre l’entité originale et son clone.

Une méthode plus propre consisterait à implémenter la méthode __clone pour les cloner également.

  • Partager sur Facebook
  • Partager sur Twitter
15 octobre 2020 à 9:55:50

Bonjour, merci pour ta réponse.

Oui effectivement c'est bien ça le problème avec le clone et les collections.. Peux-tu me donner plus de précision sur ta solution ?

Je dois parcourir chacune des collections de mon entité clonée et faire un clone pour chaque valeur ?

  • Partager sur Facebook
  • Partager sur Twitter
Anonyme
15 octobre 2020 à 10:03:34

Je ne suis pas sûr mais a priori cloner les collections devrait suffire 🤔 pas besoin de les parcourir donc.
  • Partager sur Facebook
  • Partager sur Twitter
15 octobre 2020 à 10:10:03

Mais comment je peux cloner les collections ? je ne vois vraiment pas comment faire. Dans quoi je vais stocker le clone ? une nouvelle variable ?

    /**
     * @ORM\ManyToMany(targetEntity=DesiFibre::class, inversedBy="warpYarns", cascade={"persist"})
     * @ORM\JoinTable(name="warp_yarn")
     */
    private $desiFibreWarpYarns;

    /**
     * @return Collection|DesiFibre[]
     */
    public function getDesiFibreWarpYarns(): Collection
    {
        return $this->desiFibreWarpYarns;
    }

    public function addDesiFibreWarpYarn(DesiFibre $desiFibreWarpYarn): self
    {
        if (!$this->desiFibreWarpYarns->contains($desiFibreWarpYarn)) {
            $this->desiFibreWarpYarns[] = $desiFibreWarpYarn;
            $desiFibreWarpYarn->addWarpYarn($this);
        }

        return $this;
    }

    public function removeDesiFibreWarpYarn(DesiFibre $desiFibreWarpYarn): self
    {
        if ($this->desiFibreWarpYarns->contains($desiFibreWarpYarn)) {
            $this->desiFibreWarpYarns->removeElement($desiFibreWarpYarn);
            $desiFibreWarpYarn->removeWarpYarn($this);
        }

        return $this;
    }

Je ne peux pas faire ça :

        // Clone de la ligne à dupliquer
        $new_hexforce = clone $hexforce;

        $new_hexforce->getDesiFibreWeftYarns() = clone $hexforce->getDesiFibreWeftYarns();




  • Partager sur Facebook
  • Partager sur Twitter
15 octobre 2020 à 10:24:59

Non, mais tu peux boucler sur la collection clonée et appeler addDesiFibreWeftYarn(), voire créer un setDesiFibreWeftYarns() si ça ne fiche pas en l'air les formulaires.

Autre possibilité récupérée ici : $entityManager->getUnitOfWork()->detach($blah).

Reste à savoir si ça sera toujours disponible dans Doctrine 3.

-
Edité par Ymox 15 octobre 2020 à 10:26:10

  • Partager sur Facebook
  • Partager sur Twitter
15 octobre 2020 à 10:31:51

[EDIT] problème résolut en rajoutant cette méthode dans mon entité :

    public function __clone() {
        if ($this->id) {
            $this->setId(null);
            $this->desiFibreWeftYarns = clone $this->desiFibreWeftYarns;
            $this->desiFibreWarpYarns = clone $this->desiFibreWarpYarns;
        }
    }

Et seulement ça suffit dans mon action :

        // Clone de la ligne à dupliquer
        $new_hexforce = clone $hexforce;

Merci à vous 2


-
Edité par romsVLM 15 octobre 2020 à 10:42:54

  • Partager sur Facebook
  • Partager sur Twitter