[RESOLU] [Symfony2+Doctrine2] clone object avec relations ManyToMany

Répondre


Cette question est un moyen d’empêcher des soumissions automatisées de formulaires par des robots.
Smileys
:D :) :( :o :shock: :? 8-) :lol: :x :P :oops: :cry: :evil: :twisted: :roll: :wink: :!: :?: :idea: :arrow: :| :mrgreen: =D> #-o =P~ :^o :non: :priere: 8-|
Voir plus de smileys
  Revue du sujet
 

  Étendre la vue Revue du sujet : [RESOLU] [Symfony2+Doctrine2] clone object avec relations ManyToMany

Re: [Symfony2+Doctrine2] clone object avec relations ManyToM

par ouckileou » 12 déc. 2013, 21:26

Pfff il suffisait juste d'initialiser une liste vide dans le clone... c'était simple je n'y avais pas pensé
public function __clone() {
        if ($this->id) {
            $this->id = null;            
            $this->classSessions = new \Doctrine\Common\Collections\ArrayCollection();
            $this->teachers = new \Doctrine\Common\Collections\ArrayCollection();
            $this->classSessions->clear();
            $this->semester = null;
        }
    }
public function cloneCourses($courseIds, $newSemester) {
        $courses = $this->getRepository()->findByIds($courseIds);
        foreach($courses as $course) {
            $newCourse = clone $course;
            $newCourse->setSemester($newSemester);
            $this->em->persist($newCourse);
            $this->em->flush();
            
            foreach($course->getTeachers() as $teacher) {
                $newCourse->addTeacher($teacher);
            }
            $this->em->persist($newCourse);
            $this->em->flush();
        }
    }
Personne ne fait de copie d'entité ici ? :)

Re: [Symfony2+Doctrine2] clone object avec relations ManyToM

par ouckileou » 11 déc. 2013, 23:56

Je suis tombé là-dessus http://www.pantovic.com/article/26/doct ... ty-cloning et en faisant ça jai eu l'erreur indiquée ici http://www.doctrine-project.org/jira/browse/DDC-2624

donc ma méthode __clone ressemble à ça :
    public function __clone() {
        if ($this->id) {
            $this->id = null;            
            if ($this->teachers instanceof PersistentCollection) {
                $this->teachers = clone $this->teachers;
                $this->teachers->setOwner($this, $this->teachers->getMapping());
            }
            $this->classSessions->clear();
            $this->semester = null;
        }
    }
Et la boucle à ça:
    public function cloneCourses($courseIds, $newSemester) {
        $courses = $this->getRepository()->findByIds($courseIds);
        foreach($courses as $course) {
            $newCourse = clone $course;
            $newCourse->setSemester($newSemester);
            $this->em->persist($newCourse);
            $this->em->flush();
            
            foreach($course->getTeachers() as $teacher) {
                $teacher->addCourse($newCourse);
                $this->em->persist($teacher);
            }
            $this->em->flush();
        }
    }
C'est le mieux que j'arrive à avoir, au moins ça ne supprime pas la relation entre les professeurs et le cours source du clone.

Mais ça n'ajoute toujours pas de relation entre ces mêmes professeurs et le cours cible du clone.

Si j'inverse en faisant ça :
foreach($course->getTeachers() as $teacher) {
                $newCourse->addTeacher($teacher);
            }
            $this->em->persist($newCourse);
            $this->em->flush();
J'ai une erreur sur la contrainte d'intégrité, ça tente d'insérer une ligne déjà présente dans ma relation cours - professeurs.

Re: [Symfony2+Doctrine2] clone object avec relations ManyToM

par ouckileou » 11 déc. 2013, 17:38

J'imagine que c'est un problème avec clone car en créeant un nouvel object Cours moi-même, et en recopiant tout, cela semble fonctionner. Mais c'est moche, et si je rajoute un champ je serai obligé de venir modifier ici aussi.
        $courses = $this->getRepository()->findByIds($courseIds);
        foreach($courses as $course) {
            $newCourse = new Course();
            $newCourse->setDayOfWeek($course->getDayOfWeek());
            $newCourse->setClassRoom($course->getClassRoom());
            $newCourse->setStartTime($course->getStartTime());
            $newCourse->setEndTime($course->getEndTime());
            $newCourse->setClassLevel($course->getClassLevel());
            $newCourse->setAlternateStartdate($course->getAlternateStartdate());
            $newCourse->setAlternateEnddate($course->getAlternateEnddate());
            $newCourse->setOrganizationBranch($course->getOrganizationBranch());
            $newCourse->setSemester($newSemester);
            $newCourse->removeAllClassSessions();
            $this->em->persist($newCourse);
            $this->em->flush();
            
            foreach($course->getTeachers() as $teacher) {
                echo 'Adding teacher: #' . $teacher->getId() . ' to course #' . $course->getId();
                $newCourse->addTeacher($teacher);
                //$teacher->addCourse($newCourse);                
                $this->em->persist($newCourse);
                //$this->em->persist($teacher);
                $this->em->flush();
            }
        }

Re: [Symfony2+Doctrine2] clone object avec relations ManyToM

par ouckileou » 11 déc. 2013, 17:16

Quand je rajoute le chargement des profs dans ma requête, comme ceci :
    public function findByIds(array $coursesIds) {
        $q = $this
                ->createQueryBuilder('c')    
                ->innerJoin('c.teachers', 't')
                ->where('c.id IN (:coursesIds)')
                ->setParameter('coursesIds', $coursesIds, Connection::PARAM_INT_ARRAY)
                ->getQuery();
        return $q->execute();        
    }
J'obtiens ensuite cette erreur :

Code : Tout sélectionner

An exception occurred while executing 'INSERT INTO teacher_course (course_id, teacher_id) VALUES (?, ?)' with params {"1":546,"2":981}: SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '546-981' for key 'PRIMARY'
Pourtant dans mon clone je set bien l'ID à null. Et le cours est sauvegardé :(

[Symfony2+Doctrine2] clone object avec relations ManyToMany

par ouckileou » 11 déc. 2013, 13:12

Hello,

dans mon outil de gestion de cours je gère des semestres auxquels sont reliés des cours. Lorsque l'administration crée un nouveau semestre, j'aimerais lui offrir la possibilité de recopier les cours du semestre en cours, car parfois le planning ne change que très peu et ce serait dommage de tout resaisir à la main.

J'ai donc quelque chose comme ça :

Code : Tout sélectionner

Semestre 1,n <-------> 1,1 Cours 1,n <-------> 1,1 Compte-rendu 1,n <-------> 1,n Formateur
Je voudrais donc cloner les objets cours, à l'exception de la liste des compte-rendus associés.

Voici mes entités.
Cours:
<?php

namespace Virgule\Bundle\MainBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Virgule\Bundle\MainBundle\Validator\Constraints as VirguleAssert;

/**
 * Course
 *
 * @ORM\Table(name="course")
 * @ORM\Entity(repositoryClass="Virgule\Bundle\MainBundle\Repository\CourseRepository")
 * @VirguleAssert\CourseNotOverlapping
 */
class Course {
    /**
     * @ORM\ManyToOne(targetEntity="Semester", inversedBy="courses")
     * @ORM\JoinColumn(name="fk_semester", referencedColumnName="id", nullable=false)
     */
    protected $semester;

    /**
     * @ORM\ManyToMany(targetEntity="Teacher", inversedBy="courses")
     * @ORM\JoinTable(name="teacher_course")
     * @Assert\NotNull
     */
    protected $teachers;


    /**
     * @ORM\OneToMany(targetEntity="ClassSession", mappedBy="course")
     */
    protected $classSessions;

    public function __clone() {
        if ($this->id) {
            $c = new Course();
            $c->id = null;
            $c->startTime = $this->startTime;
            $c->endTime = $this->endTime;
            $c->classRoom = $this->classRoom;
            foreach($this->getTeachers() as $teacher) {
                $c->addTeacher($teacher);
            }   
            
            return $c;
        }
    }
    
}
Formateur:
<?php

namespace Virgule\Bundle\MainBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use FOS\UserBundle\Model\User as BaseUser;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;

/**
 * Virgule\Bundle\MainBundle\Entity\Teacher
 *
 * @ORM\Table(name="teacher")
 * @ORM\Entity(repositoryClass="Virgule\Bundle\MainBundle\Repository\TeacherRepository")
 * @UniqueEntity(fields="username", message="Ce nom d'utilisateur est déjà pris")
 * @UniqueEntity(fields="email", message="Cette adresse email est déjà utilisée") 
*/
class Teacher extends BaseUser {
  
    /**
     * @var integer $id
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @ORM\ManyToMany(targetEntity="Course", mappedBy="teachers", cascade={"persist"})
     */
    private $courses;

}
La copie se passe bien pour les champs simples (heure de début, heure de fin etc) mais pour la relation avec les formateurs, elle est toujours vide, rien n'est sauvegardé dans ma table de relation.

J'ai à peu près tout essayé: surcharger la méthode clone, mettre "cascade=persist" d'un côté ou de l'autre, ajouter le cours dans $cours côté formateur, ou ajouter le formateur dans $teachers côté cours, les deux à la fois, j'en suis à ça et toujours rien :
public function copyCourses($courseIds, $newSemester) {
        $courses = $this->getRepository()->findByIds($courseIds);
        foreach($courses as $course) {
            $newCourse = clone $course;
            $newCourse->setSemester($newSemester);
            $newCourse->removeAllClassSessions();
            $this->em->persist($newCourse);
            $this->em->flush();
            
            foreach($course->getTeachers() as $teacher) {
                $teacher->addCourse($newCourse);                
                $this->em->persist($teacher);
                $this->em->flush();
            }
        }
    }
La méthode copy n'est pas implémentée dans Doctrine 2 : http://www.doctrine-project.org/api/orm ... ml#722-738

Je ne comprends pas ça me paraît pourtant simple.

Merci de votre aide ! :)