2012-01-31 12 views
17

Thứ nhất, câu hỏi này cũng tương tự như How to re-save the entity as another row in Doctrine 2Symfony2/Học thuyết: Làm thế nào để lưu lại một thực thể với một OneToMany như một tầng hàng mới

Sự khác biệt là tôi đang cố gắng để lưu các dữ liệu trong một thực thể mà có mối quan hệ OneToMany. Tôi muốn lưu lại thực thể như là một hàng mới trong thực thể cha (ở phía "một") và sau đó là các hàng mới trong mỗi đứa trẻ tiếp theo (ở phía "nhiều").

Tôi đã sử dụng một ví dụ khá đơn giản về Lớp học có nhiều học sinh để đơn giản.

Vì vậy, tôi có thể có ClassroomA với id = 1 và nó có 5 học sinh (id từ 1 đến 5). Tôi muốn biết làm thế nào tôi có thể, trong Doctrine2, lấy Entity đó và lưu lại nó vào cơ sở dữ liệu (sau khi thay đổi dữ liệu tiềm năng) tất cả với các ID mới trong suốt và các hàng gốc không bị ảnh hưởng trong suốt quá trình lưu/xả.

Cho phép đầu tiên xác định Thực thể học thuyết của chúng tôi.

Các lớp Entity:

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 ============ 

} 

Các học sinh Entity:

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 ============ 
} 

Và chức năng hành động chung của chúng tôi:

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'); 
} 

Tôi đã thử sử dụng clone nhưng điều đó chỉ lưu mối quan hệ cha mẹ (Lớp học trong ví dụ của chúng tôi) với một ID mới, trong khi dữ liệu trẻ em (học sinh) được cập nhật ag ainst các ID gốc.

Cảm ơn bạn trước sự hỗ trợ.

Trả lời

30

Điều với clone là ...

Khi một đối tượng được nhân bản, PHP 5 sẽ thực hiện một bản sao cạn của tất cả các thuộc tính của đối tượng. Bất kỳ thuộc tính nào là tham chiếu đến các biến khác, sẽ vẫn là tham chiếu.

Nếu bạn đang sử dụng Học thuyết> = 2.0.2, bạn có thể thực hiện __clone tùy chỉnh của riêng bạn() phương pháp:

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); 
    } 
} 

Chú ý: trước khi học thuyết 2.0.2 bạn không thể thực hiện một phương pháp __clone() tại của bạn thực thể như lớp proxy được tạo ra thực hiện riêng của mình __clone() mà không kiểm tra hoặc gọi parent::__clone(). Vì vậy, bạn sẽ phải thực hiện một phương pháp riêng biệt cho điều đó như clonePupils() (trong Classroom) thay vào đó và gọi sau khi bạn sao chép thực thể. Dù bằng cách nào, bạn cũng có thể sử dụng cùng một mã trong các phương thức __clone() hoặc clonePupils() của mình.

Khi bạn sao chép lớp cha mẹ của bạn, chức năng này sẽ tạo một bộ sưu tập mới đầy đủ các bản sao đối tượng con là tốt.

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

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

Bạn có lẽ sẽ muốn thác tồn tại về thu $pupils của bạn để làm cho sự bền bỉ dễ dàng hơn, ví dụ như

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

Điều đó đã làm nó, cảm ơn bạn. Tôi đã hy vọng tránh phải lặp lại trong một chức năng nhưng đây là giải pháp đơn giản cuối cùng. Tôi thấy lớp EntityManager của Doctrine có chức năng "sao chép" được mô tả là "Tạo bản sao của thực thể đã cho. Có thể tạo bản sao nông hoặc sâu". Điểm duy nhất là hàm có một dòng mà ném ngoại lệ "Không được triển khai". – dividebyzeroZA

+2

FYI trong Doctrine2 hiện tại bạn có thể thực hiện '__clone' của riêng bạn và nó sẽ được gọi. – jcbwlkr

-3

Tôi làm điều này:

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

Điều này không giống như nó sẽ tạo ra các cá thể học sinh mới; nó sẽ chia sẻ mỗi học sinh giữa hai lớp học. –

2

tôi đã làm nó như thế này và nó hoạt động tốt.

Thực thể nhân bản bên trong chúng tôi có ma thuật __clone(). Ở đó chúng tôi cũng không quên một-nhiều của chúng tôi.

/** 
* 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; 
} 

Một nơi nào đó chỉ cần sao chép nó:

// 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();