使用相关实体进行深度克隆Doctrine实体

55

我已经创建了一个实体 A,它与实体B之间存在 OneToMany 关系,而实体 B 又与实体 C 之间存在 OneToMany 关系。

我必须克隆此 A 实体,并将其设置为具有新 ID 的数据库中。此外,所有深层关系也应使用新 ID 进行克隆。

我尝试过将 A 的 ID 设置为 null

$A = clone $A_original;
$A->setId(null);
$em->persist($A);

它在A表中创建新记录,但不会在BC表中创建。

我应该怎么做才能完整复制A实体?

4个回答

90
你需要在实体类中实现一个 __clone() 方法,将 id 设置为 null,并在需要的情况下克隆关系。因为如果你保留相关对象中的id,则会认为你的新实体 A 与现有实体 BC 存在关系。 A 的克隆方法:
public function __clone() {
    if ($this->id) {
        $this->setId(null);
        $this->B = clone $this->B;
        $this->C = clone $this->C;
    }
}

BC的克隆方法:

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

https://groups.google.com/forum/?fromgroups=#!topic/doctrine-user/Nu2rayrDkgQ

https://doctrine-orm.readthedocs.org/en/latest/cookbook/implementing-wakeup-or-clone.html

根据coder4show的评论,对于在A上存在一个一对多关系$this->M,并且这个关系是一个ArrayCollection,需要实现一个克隆方法:

public function __clone() {
    if ($this->id) {
        $this->setId(null);

        // cloning the relation M which is a OneToMany
        $mClone = new ArrayCollection();
        foreach ($this->M as $item) {
            $itemClone = clone $item;
            $itemClone->setA($this);
            $mClone->add($itemClone);
        }
        $this->M = $mClone;
    }
}

1
@coder4show,这是因为您正在克隆ArrayCollection而不是其中的元素。我已经为OneToMany关系添加了一个克隆方法。 - flec
1
在这个例子中,OneToMany关系将无法正确地被克隆,因为克隆的相关实体仍然引用旧实体。请参见此答案以获取一个可行的示例。 - Richard
1
@Richard 谢谢你,你说得对。我已经更新了示例以使其正常工作。 - flec
3
使用$this->setId(null)而不是$this->id = null有特别的原因吗? - Pierre de LESPINAY
2
@PierredeLESPINAY 如果你有一个setter,我建议使用它,因为在更改id时可能会触发一些其他逻辑。除此之外,我没有特别的理由使用setter。对于克隆,它应该具有相同的效果。 - flec
显示剩余3条评论

9

还有一个模块可以实现这个功能,它叫做DeepCopy:

https://github.com/myclabs/DeepCopy

$deepCopy = new DeepCopy();
$myCopy   = $deepCopy->copy($myObject);

您还可以添加过滤器来自定义复制过程。


嗨 @Ben,你能看一下这个链接吗:this - ReynierPM
3
我发现这个程序异常地有 bug,自己编写“clone”方法更快。 - Jake N

3

我无法使用DeepClone(它需要PHP 7.1+),因此我找到了更简单的方法来克隆实体__clone方法中的关系。

$this->tags = new ArrayCollection($this->tags->toArray());

1
完美!它可以工作。我正在使用可迭代对象,所以看起来像这样:$this->foos = \iterable_to_array($this->foos); - Athos

0
一个干净的方法来克隆一个ArrayCollection:
$this->setItems(
    $this->getItems()->map(function (Item $item) {
        return clone $item;
    })
);


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接