学说 2 @Version 不起作用
Doctrine 2 @Version doesn't work
我尝试使用 Doctrine2 实现可版本化的 ORM。
一切正常。当我更新现有条目时,旧条目会插入到 *_version table.
但是当我在另一个请求中更新条目时(因此 EntityManager
的新实例)旧条目将不再写入 *_version
table 尽管基本 table 中的条目更新没有任何问题(甚至版本号也增加了 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::$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
注释,这意味着 Product
和 ProductVersion
都将由 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 连接将第二次刷新替换为直接插入。
我尝试使用 Doctrine2 实现可版本化的 ORM。
一切正常。当我更新现有条目时,旧条目会插入到 *_version table.
但是当我在另一个请求中更新条目时(因此 EntityManager
的新实例)旧条目将不再写入 *_version
table 尽管基本 table 中的条目更新没有任何问题(甚至版本号也增加了 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::$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
注释,这意味着 Product
和 ProductVersion
都将由 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 连接将第二次刷新替换为直接插入。