php - 学说 2 @Version 不起作用

标签 php orm doctrine-orm

我尝试使用 Doctrine2 实现可版本化的 ORM。

一切正常。当我更新现有条目时,旧条目会插入到 *_version 表中。

但是当我更新另一个请求中的条目时(因此更新了 EntityManager 的新实例),旧条目将不再写入 *_version 表,尽管基本表中的条目得到更新没有任何问题(甚至版本号增加 1)。

我想向您展示我非常简单的可版本化 ORM:

更新:下面的示例代码现在可以使用了!

Also check my Gist with the logEntityVersion helper method.

ProductBase.php

trait ProductBase
{
    /** 
     * @ORM\Id 
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */
    protected $id;
    /** 
     * @ORM\Column(type="string") 
     */
    protected $name;

    // getters and setters here
}

Product.php

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;

/**
 * Product
 *
 * @ORM\Entity
 * @ORM\Table(name="product")
 * @ORM\HasLifeCycleCallbacks
 */
class Product
{
    use ProductBase;

    /**
     * @ORM\OneToMany(targetEntity="ProductVersion", mappedBy="product", cascade={"persist"})
     */
    private $auditLog;

    /**
     * @ORM\Column(type="integer")
     * @ORM\Version
     */
    private $version = 1;

    public function __construct()
    {
        $this->auditLog = new ArrayCollection();
    }

    public function logVersion()
    {
        echo sprintf("Creating a new version for ID %s, version %s\n", $this->getId(), $this->getVersion());
        $this->auditLog[] = new ProductVersion($this);
    }

    // Version getter and setter
}

ProductVersion.php

use Doctrine\ORM\Mapping as ORM;

/**
 * ProductVersion
 *
 * @ORM\Entity
 * @ORM\Table(name="product_version")
 */
class ProductVersion
{
    use ProductBase;

    /**
     * @ORM\ManyToOne(targetEntity="Product", inversedBy="auditLog")
     */
    private $product;

    /**
     * @ORM\Column(type="integer")
     */
    private $version;

    public function __construct(Product $product)
    {
        $this->product = $product;
        $this->name = $product->getName();
        $this->version = $product->getVersion();

        var_dump($product->getVersion());
    }

    // Version getter and setter
}

这是插入新条目并更新它以创建新版本的代码:

// Insert new product
$this->entityManager->beginTransaction();
$this->entityManager->flush();

$product = new Product();
$product->setName('Product V1');

$this->entityManager->persist($product);
$this->entityManager->flush();

$this->entityManager->commit();

$productId = $product->getId();

echo "Created Product with ID " . $product->getId() . "\n";

/** @var Product $foo */
$foo = $this->entityManager->getRepository('orm\Product')->find($productId);

// Log version (Product V1)
$this->entityManager->beginTransaction();
$this->entityManager->flush();
$foo->logVersion();
$this->entityManager->flush();
$this->entityManager->commit();

// Update 1
$foo->setName('Product V2');
$this->entityManager->flush();

// Log version (Product V2)
$this->entityManager->beginTransaction();
$this->entityManager->flush();
$foo->logVersion();
$this->entityManager->flush();
$this->entityManager->commit();

// Update 2 
$foo->setName('Product V3');
$this->entityManager->flush();

架构生成

$tools = new SchemaTool($this->entityManager);

var_dump($tools->getCreateSchemaSql(array(
    $this->entityManager->getClassMetadata('orm\Product'),
    $this->entityManager->getClassMetadata('orm\ProductVersion')
)));

最佳答案

我看到你的代码有几个问题,不幸的是这个概念也是如此:

数组与集合

您已将 Product::$auditLog 定义为一个数组,但它必须是一个集合。改用这个:

class Product
{
    private $auditLog;

    public function __construct()
    {
        $this->auditLog = new \Doctrine\Common\Collections\ArrayCollection();
    }

关联映射

您已将 Product::$auditLog 定义为由 ProductVersion::$product 映射,但尚未定义 ProductVersion::$productProduct::$auditLog 反转。像这样修复它:

class ProductVersion
{
    /** @ORM\ManyToOne(targetEntity="Product", inversedBy="auditLog") */
    private $product;

此外,请验证您的映射和数据库架构。每个错误都可能导致意想不到的结果。

$ doctrine orm:validate-schema          // plain Doctrine2
$ app/console doctrine:schema:validate  // Symfony2

版本控制

你在 trait 中使用了 @Version 注解,这意味着 ProductProductVersion 都将由 Doctrine 进行版本控制。由于 @Version 用于乐观锁定,我怀疑这是否是您真正想要的。

不应更新(或删除)审核日志中的记录,而只能添加。所以锁定记录在这里没有任何意义。

请删除 ProductBase::$version 并添加:

  • Product::$version 带有 @Version 注释。
  • ProductVersion::$version 没有 @Version 注释。

现在只有 Product 将由 Doctrine 进行版本控制,ProductVersion 将只包含所记录产品的版本号。

预更新事件

您正在使用 @PrePersist@PreUpdate 生命周期回调来填充审计日志。 @PreUpdate 将成为一个问题,因为 docs状态:

Changes to associations of the updated entity are never allowed in this event

然而,您正在通过向集合中添加一条记录来更改 Product::$auditLog 关联。

您必须移动此逻辑,关于到哪里有很多选择:

一个选项可能是手动启动事务,刷新(并跟踪版本化实体),创建日志条目,再次刷新,提交事务。或者,您可以使用 DBAL 连接将第二次刷新替换为直接插入。

关于php - 学说 2 @Version 不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28561434/

相关文章:

javascript - 将 JS 变量传递给 PHP

php - laravel 5.4 crud 中的更新方法是什么?

php - Python 对象输出到 PHP 数组

python - 同时使用两个注释表达式时出现错误结果

java - 无法使用 hibernate 5.0 将 List<String> 插入到 postgres 9.4 表中,其中要插入的列的类型为字符 varying[]

php - Doctrine 2 多个选择元素、别名、日期格式

php - fatal error : Class 'TableRows' not found in

java - 如何使用 JPA 2、Hibernate 3.5 处理选择字段

mysql - QueryBuilder 和子条件

php - 违反完整性约束 : 1062 Duplicate entry for utf8_unicode_ci collation