2012-01-31 11 views
17

Po pierwsze, kwestia ta jest podobna do How to re-save the entity as another row in Doctrine 2Symfony2/Doctrine: Jak ponownie zapisać podmiot z OneToMany jako kaskadowych nowego wiersza

Różnica polega na tym, że próbuję zapisać dane w obrębie danego podmiotu ma związek OneToMany. Chciałbym ponownie zapisać encję jako nowy wiersz w jednostce nadrzędnej (po stronie "jednej"), a następnie jako nowe wiersze w każdym następnym potomku (po stronie "wiele").

Użyłem całkiem prostego przykładu Classroom, w którym wielu uczniów ma prostotę.

Tak więc ja może mieć ClassroomA z id = 1 i ma 5 uczniów (ids 1 do 5). Chciałbym wiedzieć, jak mogłem, w ramach Doctrine2, zabrać tę encję i ponownie zapisać ją w bazie danych (po potencjalnych zmianach danych) z nowymi identyfikatorami w całym tekście, a pierwotne wiersze pozostały nietknięte podczas utrzymywania/koloru.

Najpierw określ nasze jednostki doktryny.

The Classroom Podmiot:

namespace Acme\TestBundle\Entity; 

use Doctrine\ORM\Mapping as ORM; 
use Doctrine\Common\Collections\ArrayCollection; 

/** 
* @ORM\Entity 
* @ORM\Table(name="classroom") 
*/ 
class Classroom 
{ 
    /** 
    * @ORM\Id 
    * @ORM\Column(type="integer") 
    * @ORM\GeneratedValue(strategy="AUTO") 
    */ 
    private $id; 

    /** 
    * @ORM\Column(type="string", length=255) 
    */ 
    private $miscVars; 

    /** 
    * @ORM\OneToMany(targetEntity="Pupil", mappedBy="classroom") 
    */ 
    protected $pupils; 

    public function __construct() 
    { 
     $this->pupils = new ArrayCollection(); 
    }  
    // ========== GENERATED GETTER/SETTER FUNCTIONS BELOW ============ 

} 

źrenicy Podmiot:

namespace Acme\TestBundle\Entity; 

use Doctrine\ORM\Mapping as ORM; 
use Doctrine\Common\Collections\ArrayCollection; 

/** 
* @ORM\Entity 
* @ORM\Table(name="pupil") 
*/ 
class Pupil 
{ 
    /** 
    * @ORM\Id 
    * @ORM\Column(type="integer") 
    * @ORM\GeneratedValue(strategy="AUTO") 
    */ 
    private $id; 

    /** 
    * @ORM\Column(type="string", length=255) 
    */ 
    private $moreVars; 

    /** 
    * @ORM\ManyToOne(targetEntity="Classroom", inversedBy="pupils") 
    * @ORM\JoinColumn(name="classroom_id", referencedColumnName="id") 
    */ 
    protected $classroom; 

    // ========== GENERATED FUNCTIONS BELOW ============ 
} 

A nasz rodzajowy funkcja Działanie:

public function someAction(Request $request, $id) 
{ 
    $em = $this->getDoctrine()->getEntityManager(); 

    $classroom = $em->find('AcmeTestBundle:Classroom', $id); 

    $form = $this->createForm(new ClassroomType(), $classroom); 

    if ('POST' === $request->getMethod()) { 
     $form->bindRequest($request); 

     if ($form->isValid()) { 
      // Normally you would do the following: 
      $em->persist($classroom); 
      $em->flush(); 

      // But how do I create a new row with a new ID 
      // Including new rows for the Many side of the relationship 

      // ... other code goes here. 
     } 
    } 

    return $this->render('AcmeTestBundle:Default:index.html.twig'); 
} 

Próbowałem za pomocą klona ale tylko zaoszczędziliśmy relacja rodzica (Classroom w naszym przykładzie) ze świeżym ID, a dane dzieci (uczniowie) zostały zaktualizowane ainst oryginalne identyfikatory.

Z góry dziękuję za wszelką pomoc.

Odpowiedz

30

Rzecz z clone jest ...

Gdy obiekt jest sklonowany, PHP 5 wykona płytkie kopie wszystkich właściwości obiektu. Wszelkie właściwości, które są odwołaniami do innych zmiennych, pozostaną odniesieniami.

Jeśli używasz Nauki> = 2.0.2, można zaimplementować swój własny __clone() metoda:

public function __clone() { 
    // Get current collection 
    $pupils = $this->getPupils(); 

    $this->pupils = new ArrayCollection(); 
    foreach ($pupils as $pupil) { 
     $clonePupil = clone $pupil; 
     $this->pupils->add($clonePupil); 
     $clonePupil->setClassroom($this); 
    } 
} 

UWAGA: przed Doctrine 2.0.2 nie można wdrożyć __clone() metodę w twojej Obiekt jako wygenerowana klasa proxy implementuje własny kod __clone(), który nie sprawdza ani nie wywołuje parent::__clone(). Musisz więc stworzyć oddzielną metodę dla tego, jak clonePupils() (w Classroom) i wywołać to po sklonowaniu encji. Tak czy inaczej, możesz użyć tego samego kodu wewnątrz metod __clone() lub clonePupils().

Po sklonowaniu klasy nadrzędnej ta funkcja utworzy również nową kolekcję pełną klonów obiektów potomnych.

$cloneClassroom = clone $classroom; 
$cloneClassroom->clonePupils(); 

$em->persist($cloneClassroom); 
$em->flush(); 

Prawdopodobnie będziesz chciał utrzymywać się kaskadowo na swojej kolekcji $pupils aby utrzymujące się łatwiejsze, np

/** 
* @ORM\OneToMany(targetEntity="Pupil", mappedBy="classroom", cascade={"persist"}) 
*/ 
protected $pupils; 
+4

że to zrobił, dziękuję. Miałem nadzieję, że uniknę pętli w funkcji, ale to było najprostsze rozwiązanie na końcu. Widziałem, że klasa EntityManager Doctrine ma funkcję "kopii" opisaną jako "Tworzy kopię danego obiektu, może tworzyć płytką lub głęboką kopię". Jedynym haczykiem jest to, że funkcja ma jedną linię, która zgłasza wyjątek "Nie zaimplementowano". – dividebyzeroZA

+2

FYI w aktualnej Doctrine2 możesz zaimplementować własny '__clone' i zostanie on wywołany. – jcbwlkr

-3

zrobić to:

if ($form->isValid()) { 
    foreach($classroom->getPupils() as $pupil) { 
     $pupil->setClassroom($classroom); 
    } 
    $em->persist($classroom); 
    $em->flush(); 
} 
+3

To nie wygląda na to, że stworzy nowe instancje Uczniów; podzieli się każdym uczniem pomiędzy dwiema salami lekcyjnymi. –

2

zrobiłem to w ten sposób i to działa w porządku.

Wewnątrz sklonowanej jednostki mamy magic __clone(). Tam też nie zapominamy o naszym jeden-do-wielu.

/** 
* Clone element with values 
*/ 
public function __clone(){ 
    // we gonna clone existing element 
    if($this->id){ 
     // get values (one-to-many) 
     /** @var \Doctrine\Common\Collections\Collection $values */ 
     $values = $this->getElementValues(); 
     // reset id 
     $this->id = null; 
     // reset values 
     $this->elementValues = new \Doctrine\Common\Collections\ArrayCollection(); 
     // if we had values 
     if(!$values->isEmpty()){ 
      foreach ($values as $value) { 
       // clone it 
       $clonedValue = clone $value; 
       // add to collection 
       $this->addElementValues($clonedValue); 
      } 
     } 
    } 
} 
/** 
* addElementValues 
* 
* @param \YourBundle\Entity\ElementValue $elementValue 
* @return Element 
*/ 
public function addElementValues(\YourBundle\Entity\ElementValue $elementValue) 
{ 
    if (!$this->getElementValues()->contains($elementValue)) 
    { 
     $this->elementValues[] = $elementValue; 
     $elementValue->setElement($this); 
    } 

    return $this; 
} 

Gdzieś po prostu sklonować go:

// Returns \YourBundle\Entity\Element which we wants to clone 
$clonedEntity = clone $this->getElement(); 
// Do this to say doctrine that we have new object 
$this->em->persist($clonedEntity); 
// flush it to base 
$this->em->flush(); 
Powiązane problemy