Symfony2/Doctrine:如何将具有 OneToMany 的实体重新保存为级联新行

发布于 2025-01-01 09:03:41 字数 2749 浏览 2 评论 0原文

首先,这个问题类似于 如何将实体重新保存为 Doctrine 2 中的另一行

区别在于,我试图将数据保存在具有 OneToMany 关系的实体中。我想将实体重新保存为父实体中的新行(在“一”侧),然后将其保存为每个后续子实体中的新行(在“多”侧)。

为了简单起见,我使用了一个非常简单的例子,即有很多学生的教室。

所以我可能有 id=1 的 ClassroomA,它有 5 个学生(id 1 到 5)。我想知道如何在 Doctrine2 中获取该实体并将其重新保存到数据库(在潜在的数据更改之后),所有这些都具有新的 ID,并且在持久/刷新期间原始行保持不变。

让我们首先定义我们的学说实体。

课堂实体:

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

}

学生实体:

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

以及我们的通用操作函数:

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

我尝试使用克隆,但仅使用新 ID 保存父关系(在我们的示例中为课堂),而儿童数据(学生)则根据原始数据进行更新身份证。

预先感谢您的任何帮助。

Firstly, this question is similar to How to re-save the entity as another row in Doctrine 2

The difference is that I'm trying to save the data within an entity that has a OneToMany relationship. I'd like to re-save the entity as a new row in the parent entity (on the "one" side) and then as new rows in each subsequent child (on the "many" side).

I've used a pretty simple example of a Classroom having many Pupils to keep it simple.

So me might have ClassroomA with id=1 and it has 5 pupils (ids 1 through 5). I'd like to know how I could, within Doctrine2, take that Entity and re-save it to the database (after potential data changes) all with new IDs throughout and the original rows being untouched during the persist/flush.

Lets first define our Doctrine Entities.

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

}

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

And our generic Action function:

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

I've tried using clone but that only saved the parent relationship (Classroom in our example) with a fresh ID, while the children data (Pupils) was updated against the original IDs.

Thanks in advance to any assistance.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

不知所踪 2025-01-08 09:03:41

克隆的事情是......

克隆对象时,PHP 5 将执行该对象所有属性的浅表复制。任何引用其他变量的属性都将保留引用。

如果您使用的是 Doctrine >= 2.0.2,您可以实现自己的自定义 __clone() 方法:

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

注意:在 Doctrine 2.0.2 之前,您无法在实体中实现 __clone() 方法作为生成的代理类实现其自己的 __clone(),它不会检查或调用 parent::__clone()。因此,您必须为此创建一个单独的方法,例如 clonePupils() (在 Classroom 中),并在克隆实体后调用该方法。无论哪种方式,您都可以在 __clone()clonePupils() 方法中使用相同的代码。

当您克隆父类时,此函数也将创建一个充满子对象克隆的新集合。

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

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

您可能希望在您的 $pupils 集合上级联持久化以使持久化更容易,例如

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

The thing with clone is...

When an object is cloned, PHP 5 will perform a shallow copy of all of the object's properties. Any properties that are references to other variables, will remain references.

If you are using Doctrine >= 2.0.2, you can implement your own custom __clone() method:

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

NOTE: before Doctrine 2.0.2 you cannot implement a __clone() method in your entity as the generated proxy class implements its own __clone() which does not check for or call parent::__clone(). So you'll have to make a separate method for that like clonePupils() (in Classroom) instead and call that after you clone the entity. Either way, you can use the same code inside your __clone() or clonePupils() methods.

When you clone your parent class, this function will create a new collection full of child object clones as well.

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

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

You'll probably want to cascade persist on your $pupils collection to make persisting easier, eg

/**
 * @ORM\OneToMany(targetEntity="Pupil", mappedBy="classroom", cascade={"persist"})
 */
protected $pupils;
残龙傲雪 2025-01-08 09:03:41

我这样做了并且效果很好。

在克隆实体内部,我们有神奇的__clone()。在那里我们也不会忘记我们的一对多

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

在某个地方克隆它:

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

I did it like this and it works fine.

Inside cloned Entity we have magic __clone(). There we also don't forget our one-to-many.

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

Somewhere just clone it:

// 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();
简单 2025-01-08 09:03:41

我这样做:

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

I do this:

if ($form->isValid()) {
    foreach($classroom->getPupils() as $pupil) {
        $pupil->setClassroom($classroom);
    }
    $em->persist($classroom);
    $em->flush();
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文