如何使用Doctrine2将实体的克隆保存为更新

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何使用Doctrine2将实体的克隆保存为更新相关的知识,希望对你有一定的参考价值。

我不喜欢使用setter,因为我不喜欢在实例化后改变对象,所以我倾向于使用返回克隆的withFoo(Foo $foo)样式,而不是:

public function withFoo(Foo $foo): self
{
    $clone = clone $this;
    $clone->foo = $foo;
    return $clone;
}

很长一段时间,这对我很有帮助,但现在我必须使用Doctrine2,它不起作用:

$foo = $fooRepository->getByBar($bar);
$foo = $foo->withBaz($baz);
$emi->flush();

这会导致我的日志出错,如下所示:

app.ERROR: An exception occurred while executing 'INSERT INTO foo (id, bar, baz) VALUES (?, ?, ?)' with params ["51f74f6e-8e20-42ec-ba21-ac3ae62658ef", "Bar", "Baz"]:  SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '51f74f6e-8e20-42ec-ba21-ac3ae62658ef' for key 'PRIMARY' [] []

如果我将我的代码更改为以下代码,它将更新没有问题:

public function withFoo(Foo $foo): self
{
    $this->foo = $foo;
    return $this;
}

现在我不得不求助于使用普通的二传手,但我不喜欢它。有没有办法做到这一点?

我想告诉它保存更新,但要使用ID来确定要更新的记录。例如,mysql中的REPLACE INTO排序。

有帮助吗?

答案

好的,感谢@Cerad在我的问题评论中提出了建议。我确实需要detach and merge功能。所以实体有withBar(Bar $bar)方法:

<?php
namespace AppEntity

use DoctrineORMMapping as ORM;

/**
 * Foo
 *
 * @ORMTable(name="foo", ...)
 * @ORMEntity
 */
class Foo
{
    /**
     * @var string
     *
     * @ORMColumn(name="id", type="string", length=36, nullable=false)
     * @ORMId
     */
    private $id;

    /**
     * @var Bar
     * 
     * @ORMColumn(name="bar", type="object" ...)
     */
    private $bar;

    public function __construct(string $id, Bar $bar)
    {
        $this->id = $id;
        $this->bar = $bar;
    }

    public function withBar(Bar $bar): self
    {
        $clone = clone $this;
        $clone->bar = $bar;
        return $clone;
    }
}

这与在没有使用Doctrine的项目中一样,通过返回原始的修改后的COPY,而不是改变它。很高兴。

接下来,FooRepository更新方法变为这样:

<?php
namespace AppRepository;

use AppEntityFoo;
use DoctrineORMEntityManagerInterface;

class FooRepository
{
    private $emi, $repository;

    public function __construct(EntityManagerInterface $emi)
    {
        $this->emi = $emi;
        $this->repository = $emi->getRepository(Foo::class);
    }

    public function updateBar(string $id, Bar $bar): Foo
    {
        // Get the `Foo` and update the `Bar`
        $foo = $this->repository->findOneBy(['id'=>$id]);
        $foo = $foo->withBar($bar);

        // Detach and merge the entity
        // You can do the detach before the `withBar()` but it makes no difference
        // as the modified entity is actually an entirely new object, so Doctrine
        // can't see it until it's merged anyway
        $this->emi->detach($foo);
        $this->emi->merge($foo);
        $this->emi->flush();

        return $foo;
    }
}

故意省略错误处理,因为这些例子无论如何都是设计的

谢谢你的指针。如果你把它写成答案,我会高兴地接受它并给你代表

以上是关于如何使用Doctrine2将实体的克隆保存为更新的主要内容,如果未能解决你的问题,请参考以下文章

如何在Doctrine2中为单个实体使用多个存储库?

更新元素的 Doctrine2 缓存

Doctrine2 存储库是保存我的实体的好地方吗?

如何使用Symfony3在Doctrine2 Entity中运行查询查询

如何更改 Doctrine2 CTI 继承中的实体类型

如何克隆 JPA 实体