条令2@版本没有;不起作用


Doctrine 2 @Version doesn't work

我尝试使用Doctrine2实现一个可版本控制的ORM。

一切都很好。更新现有条目时,旧条目将插入到*_version表中。

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

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

更新:下面的示例代码现在可以工作了

同时使用logEntityVersion辅助方法检查我的Gist。

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::$product定义为由Product::$auditLog反转。这样修复:

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

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

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

版本控制

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

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

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

  • 带有CCD_ 15注释的CCD_
  • 不带@Version注释的ProductVersion::$version

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

PreUpdate事件

您正在使用@PrePersist@PreUpdate生命周期回调来填充审核日志。@PreUpdate将成为一个问题,正如文档所说:

在这种情况下,不允许更改更新实体的关联

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

您必须移动此逻辑,并且有许多选项可用于的位置:

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