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

时间:2018-06-06 19:07:44

标签: php doctrine-orm orm

我不喜欢使用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

任何帮助?

1 个答案:

答案 0 :(得分:0)

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

<?php
namespace App\Entity

use Doctrine\ORM\Mapping as ORM;

/**
 * Foo
 *
 * @ORM\Table(name="foo", ...)
 * @ORM\Entity
 */
class Foo
{
    /**
     * @var string
     *
     * @ORM\Column(name="id", type="string", length=36, nullable=false)
     * @ORM\Id
     */
    private $id;

    /**
     * @var Bar
     * 
     * @ORM\Column(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 App\Repository;

use App\Entity\Foo;
use Doctrine\ORM\EntityManagerInterface;

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

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

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