Doctrine 忽略 property/column
Doctrine ignores a property/column
我正在将基于 Zend\Db
的应用程序迁移到 Doctrine 2。
问题出在实体 AuditLog
上。在迁移之前,它一直存在 Zend\Db
(请参阅代码块 1
)。
现在实体 AuditLog
有多个子 类,它们一起实现了 Single Table Inheritance
模式(参见代码块 2
)。
自从我切换到 Doctrine 后,我应该只需要创建一个适当的 AuditLog
对象并简单地 persist(...)
它。事实上,这是我正在尝试的(参见代码块 3
)。但是现在我遇到了 AuditLog
的 属性 $resourceId
的问题:Doctrine 似乎忽略了它。 属性 包含正确的值(我在调试器中看到它),但 INSERT
语句没有得到它,看起来像:
INSERT INTO audit_log
(resource_id, action, datetime, user_id, resource_type)
VALUES
(NULL, 'order.created', NULL, 1, 'order');
结果是:实体被持久化,但是 resource_id
是空的。
为什么 Doctrine 忽略这个 属性 以及如何保存它?
代码
1
坚持Zend\Db
AuditLogger
class AuditLogger extends AbstractPlugin
{
...
public function log($resourceType = null, $resourceId = null, $action = null, $userId = null)
{
$auditLog = new AuditLog();
$auditLog->setResourceType($resourceType);
$auditLog->setResourceId($resourceId);
$auditLog->setAction($action);
...
$auditLog->setUser($this->user);
$this->auditLogService->create($auditLog);
// The AuditLogService calls then the AuditLogMapper#create(...).
}
}
AuditLogMapper
class AuditLogMapper extends AbstractMapper implements AuditLogMapperInterface
{
...
public function create(AuditLog $dataObject)
{
$data = [];
// data retrieved directly from the input
$data['resource_type'] = $dataObject->getResourceType();
$data['resource_id'] = $dataObject->getResourceId();
$data['action'] = $dataObject->getAction();
$data['datetime'] = $dataObject->getDatetime();
$data['user_id'] = $dataObject->getUser()->getId();
$action = new Insert('audit_log');
unset($data['id']);
$action->values($data);
$sql = new Sql($this->dbAdapter);
$statement = $sql->prepareStatementForSqlObject($action);
$result = $statement->execute();
if ($result instanceof ResultInterface) {
$newId = $result->getGeneratedValue() ?: $dataObject->getId();
if ($newId) {
$dataObject->setId($newId);
}
return $dataObject;
}
throw new \Exception('Database error in ' . __METHOD__);
}
}
2
实体
AuditLog
use Doctrine\ORM\Mapping as ORM;
/**
* AuditLog
*
* @ORM\Table(
* name="audit_log",
* indexes={
* @ORM\Index(name="fk_audit_log_user_idx", columns={"user_id"})
* }
* )
* @ORM\Entity
* @ORM\InheritanceType("SINGLE_TABLE")
* @ORM\DiscriminatorColumn(name="resource_type", type="string")
* @ORM\DiscriminatorMap({
* "" = "AuditLog",
* "order" = "AuditLogOrder",
* "server" = "AuditLogServer",
* "cluster" = "AuditLogCluster"
* })
*/
class AuditLog extends AbstractDataObject
{
/** @var string */
const RESSOURCE_TYPE_ORDER = 'order';
/** @var string */
const RESSOURCE_TYPE_SERVER = 'server';
/** @var string */
const RESSOURCE_TYPE_CLUSTER = 'cluster';
/** @var string */
const ACTION_ORDER_CREATED = 'order.created';
/** @var string */
const ACTION_ORDER_SUBMITTED = 'order.submitted';
/** @var string */
const ACTION_ORDER_EDITING_STARTED = 'order.editing_started';
/** @var string */
const ACTION_ORDER_UPDATED = 'order.updated';
/** @var string */
const ACTION_ORDER_CANCELED = 'order.canceled';
/** @var string */
const ACTION_ORDER_CHECKING_STARTED = 'order.checking_started';
/** @var string */
const ACTION_ORDER_ACCEPTED = 'order.accepted';
/** @var string */
const ACTION_ORDER_DECLINED = 'order.declined';
/** @var string */
const ACTION_ORDER_COMPLETED = 'order.completed';
/** @var string */
const ACTION_ORDER_EXPORTED = 'order.exported';
/** @var string */
const ACTION_SERVER_VIRTUAL_NODE_NAME_ADDED = 'server.virtual_node_name_added';
/** @var string */
const ACTION_CLUSTER_CREATED = 'cluster.created';
/**
* @var integer
*
* @ORM\Column(name="id", type="integer", nullable=false)
* @ORM\Id
* @ORM\GeneratedValue(strategy="IDENTITY")
*/
protected $id;
/**
* @var string
*/
protected $resourceType;
/**
* @var string
*
* @ORM\Column(name="resource_id", type="string", length=50, nullable=true)
*/
protected $resourceId;
/**
* @var string
*
* @ORM\Column(name="action", type="string", nullable=true)
*/
protected $action;
/**
* @var \DateTime
*
* @ORM\Column(name="datetime", type="datetime", nullable=false)
*/
protected $datetime;
/**
* @var User
*
* @ORM\ManyToOne(targetEntity="User")
*/
protected $user;
// access methods ...
}
AuditLogOrder
use Doctrine\ORM\Mapping as ORM;
/**
* AuditLogOrder
*
* @ORM\Table(name="audit_log")
* @ORM\Entity
*/
class AuditLogOrder extends AuditLog
{
/**
* @var Order
*
* @ORM\ManyToOne(targetEntity="Order")
* @ORM\JoinColumn(name="resource_id", referencedColumnName="id")
*/
protected $order;
// access methods ...
}
AuditLogServer
use Doctrine\ORM\Mapping as ORM;
/**
* AuditLogServer
*
* @ORM\Table(name="audit_log")
* @ORM\Entity
*/
class AuditLogServer extends AuditLog
{
// ...
}
AuditLogCluster
use Doctrine\ORM\Mapping as ORM;
/**
* AuditLogCluster
*
* @ORM\Table(name="audit_log")
* @ORM\Entity
*/
class AuditLogCluster extends AuditLog
{
// ...
}
3
坚持教义
AuditLogger
class AuditLogger extends AbstractPlugin
{
...
public function log($resourceType = null, $resourceId = null, $action = null, $userId = null)
{
switch ($resourceType) {
case AuditLog::RESSOURCE_TYPE_ORDER:
$auditLog = new AuditLogFileTransferRequest();
break;
case AuditLog::RESSOURCE_TYPE_SERVER:
$auditLog = new AuditLogServer();
break;
case AuditLog::RESSOURCE_TYPE_CLUSTER:
$auditLog = new AuditLogCluster();
break;
default:
$auditLog = new AuditLog();
}
$auditLog->setResourceType($resourceType);
$auditLog->setResourceId($resourceId);
$auditLog->setAction($action);
...
$auditLog->setUser($this->user);
$this->auditLogService->create($auditLog);
// The AuditLogService calls then the AuditLogMapper#create(...).
}
}
AuditLogMapper
class AuditLogMapper extends AbstractMapper implements AuditLogMapperInterface
{
...
public function create(AuditLog $dataObject)
{
$currentUser = $this->entityManager->getRepository(User::class)->find(
$dataObject->getUser()->getId()
);
$dataObject->setUser($currentUser);
$this->entityManager->persist($dataObject);
$this->entityManager->flush();
return $dataObject;
}
}
由于 resource_id
用作 JoinColumn
/**
* @var Order
*
* @ORM\ManyToOne(targetEntity="Order")
* @ORM\JoinColumn(name="resource_id", referencedColumnName="id")
*/
protected $order;
不是普通的属性了,可以直接设置。需要改为设置相关实体:
...
class AuditLogger extends AbstractPlugin
{
...
/**
* @var User
*/
protected $user;
public function __construct(
AuditLogServiceInterface $auditLogService,
OrderInterface $order,
ServerServiceInterface $serverService,
ClusterServiceInterface $clusterService,
User $user = null
) {
$this->auditLogService = $auditLogService;
$this->order = $order;
$this->serverService = $serverService;
$this->clusterService = $clusterService;
$this->user = $user;
}
public function log($resourceType = null, $resourceId = null, $action = null, $userId = null)
{
switch ($resourceType) {
case AuditLog::RESSOURCE_TYPE_ORDER:
$auditLog = new AuditLogFileTransferRequest();
$resource = $this->order->findOne($resourceId);
$auditLog->setFileTransferRequest($resource);
break;
case AuditLog::RESSOURCE_TYPE_SERVER:
$auditLog = new AuditLogServer();
$resource = $this->serverService->findOne($resourceId);
$auditLog->setServer($resource);
break;
case AuditLog::RESSOURCE_TYPE_CLUSTER:
$auditLog = new AuditLogCluster();
$resource = $this->clusterService->findOne($resourceId);
$auditLog->setCluster($resource);
break;
default:
$auditLog = new AuditLog();
}
$auditLog->setAction($action);
if ($userId) {
$user = new User();
$user->setId($userId);
$auditLog->setUser($user);
} elseif ($this->user) {
$auditLog->setUser($this->user);
}
$this->auditLogService->create($auditLog);
}
}
我正在将基于 Zend\Db
的应用程序迁移到 Doctrine 2。
问题出在实体 AuditLog
上。在迁移之前,它一直存在 Zend\Db
(请参阅代码块 1
)。
现在实体 AuditLog
有多个子 类,它们一起实现了 Single Table Inheritance
模式(参见代码块 2
)。
自从我切换到 Doctrine 后,我应该只需要创建一个适当的 AuditLog
对象并简单地 persist(...)
它。事实上,这是我正在尝试的(参见代码块 3
)。但是现在我遇到了 AuditLog
的 属性 $resourceId
的问题:Doctrine 似乎忽略了它。 属性 包含正确的值(我在调试器中看到它),但 INSERT
语句没有得到它,看起来像:
INSERT INTO audit_log
(resource_id, action, datetime, user_id, resource_type)
VALUES
(NULL, 'order.created', NULL, 1, 'order');
结果是:实体被持久化,但是 resource_id
是空的。
为什么 Doctrine 忽略这个 属性 以及如何保存它?
代码
1
坚持Zend\Db
AuditLogger
class AuditLogger extends AbstractPlugin
{
...
public function log($resourceType = null, $resourceId = null, $action = null, $userId = null)
{
$auditLog = new AuditLog();
$auditLog->setResourceType($resourceType);
$auditLog->setResourceId($resourceId);
$auditLog->setAction($action);
...
$auditLog->setUser($this->user);
$this->auditLogService->create($auditLog);
// The AuditLogService calls then the AuditLogMapper#create(...).
}
}
AuditLogMapper
class AuditLogMapper extends AbstractMapper implements AuditLogMapperInterface
{
...
public function create(AuditLog $dataObject)
{
$data = [];
// data retrieved directly from the input
$data['resource_type'] = $dataObject->getResourceType();
$data['resource_id'] = $dataObject->getResourceId();
$data['action'] = $dataObject->getAction();
$data['datetime'] = $dataObject->getDatetime();
$data['user_id'] = $dataObject->getUser()->getId();
$action = new Insert('audit_log');
unset($data['id']);
$action->values($data);
$sql = new Sql($this->dbAdapter);
$statement = $sql->prepareStatementForSqlObject($action);
$result = $statement->execute();
if ($result instanceof ResultInterface) {
$newId = $result->getGeneratedValue() ?: $dataObject->getId();
if ($newId) {
$dataObject->setId($newId);
}
return $dataObject;
}
throw new \Exception('Database error in ' . __METHOD__);
}
}
2
实体
AuditLog
use Doctrine\ORM\Mapping as ORM;
/**
* AuditLog
*
* @ORM\Table(
* name="audit_log",
* indexes={
* @ORM\Index(name="fk_audit_log_user_idx", columns={"user_id"})
* }
* )
* @ORM\Entity
* @ORM\InheritanceType("SINGLE_TABLE")
* @ORM\DiscriminatorColumn(name="resource_type", type="string")
* @ORM\DiscriminatorMap({
* "" = "AuditLog",
* "order" = "AuditLogOrder",
* "server" = "AuditLogServer",
* "cluster" = "AuditLogCluster"
* })
*/
class AuditLog extends AbstractDataObject
{
/** @var string */
const RESSOURCE_TYPE_ORDER = 'order';
/** @var string */
const RESSOURCE_TYPE_SERVER = 'server';
/** @var string */
const RESSOURCE_TYPE_CLUSTER = 'cluster';
/** @var string */
const ACTION_ORDER_CREATED = 'order.created';
/** @var string */
const ACTION_ORDER_SUBMITTED = 'order.submitted';
/** @var string */
const ACTION_ORDER_EDITING_STARTED = 'order.editing_started';
/** @var string */
const ACTION_ORDER_UPDATED = 'order.updated';
/** @var string */
const ACTION_ORDER_CANCELED = 'order.canceled';
/** @var string */
const ACTION_ORDER_CHECKING_STARTED = 'order.checking_started';
/** @var string */
const ACTION_ORDER_ACCEPTED = 'order.accepted';
/** @var string */
const ACTION_ORDER_DECLINED = 'order.declined';
/** @var string */
const ACTION_ORDER_COMPLETED = 'order.completed';
/** @var string */
const ACTION_ORDER_EXPORTED = 'order.exported';
/** @var string */
const ACTION_SERVER_VIRTUAL_NODE_NAME_ADDED = 'server.virtual_node_name_added';
/** @var string */
const ACTION_CLUSTER_CREATED = 'cluster.created';
/**
* @var integer
*
* @ORM\Column(name="id", type="integer", nullable=false)
* @ORM\Id
* @ORM\GeneratedValue(strategy="IDENTITY")
*/
protected $id;
/**
* @var string
*/
protected $resourceType;
/**
* @var string
*
* @ORM\Column(name="resource_id", type="string", length=50, nullable=true)
*/
protected $resourceId;
/**
* @var string
*
* @ORM\Column(name="action", type="string", nullable=true)
*/
protected $action;
/**
* @var \DateTime
*
* @ORM\Column(name="datetime", type="datetime", nullable=false)
*/
protected $datetime;
/**
* @var User
*
* @ORM\ManyToOne(targetEntity="User")
*/
protected $user;
// access methods ...
}
AuditLogOrder
use Doctrine\ORM\Mapping as ORM;
/**
* AuditLogOrder
*
* @ORM\Table(name="audit_log")
* @ORM\Entity
*/
class AuditLogOrder extends AuditLog
{
/**
* @var Order
*
* @ORM\ManyToOne(targetEntity="Order")
* @ORM\JoinColumn(name="resource_id", referencedColumnName="id")
*/
protected $order;
// access methods ...
}
AuditLogServer
use Doctrine\ORM\Mapping as ORM;
/**
* AuditLogServer
*
* @ORM\Table(name="audit_log")
* @ORM\Entity
*/
class AuditLogServer extends AuditLog
{
// ...
}
AuditLogCluster
use Doctrine\ORM\Mapping as ORM;
/**
* AuditLogCluster
*
* @ORM\Table(name="audit_log")
* @ORM\Entity
*/
class AuditLogCluster extends AuditLog
{
// ...
}
3
坚持教义
AuditLogger
class AuditLogger extends AbstractPlugin
{
...
public function log($resourceType = null, $resourceId = null, $action = null, $userId = null)
{
switch ($resourceType) {
case AuditLog::RESSOURCE_TYPE_ORDER:
$auditLog = new AuditLogFileTransferRequest();
break;
case AuditLog::RESSOURCE_TYPE_SERVER:
$auditLog = new AuditLogServer();
break;
case AuditLog::RESSOURCE_TYPE_CLUSTER:
$auditLog = new AuditLogCluster();
break;
default:
$auditLog = new AuditLog();
}
$auditLog->setResourceType($resourceType);
$auditLog->setResourceId($resourceId);
$auditLog->setAction($action);
...
$auditLog->setUser($this->user);
$this->auditLogService->create($auditLog);
// The AuditLogService calls then the AuditLogMapper#create(...).
}
}
AuditLogMapper
class AuditLogMapper extends AbstractMapper implements AuditLogMapperInterface
{
...
public function create(AuditLog $dataObject)
{
$currentUser = $this->entityManager->getRepository(User::class)->find(
$dataObject->getUser()->getId()
);
$dataObject->setUser($currentUser);
$this->entityManager->persist($dataObject);
$this->entityManager->flush();
return $dataObject;
}
}
由于 resource_id
用作 JoinColumn
/**
* @var Order
*
* @ORM\ManyToOne(targetEntity="Order")
* @ORM\JoinColumn(name="resource_id", referencedColumnName="id")
*/
protected $order;
不是普通的属性了,可以直接设置。需要改为设置相关实体:
...
class AuditLogger extends AbstractPlugin
{
...
/**
* @var User
*/
protected $user;
public function __construct(
AuditLogServiceInterface $auditLogService,
OrderInterface $order,
ServerServiceInterface $serverService,
ClusterServiceInterface $clusterService,
User $user = null
) {
$this->auditLogService = $auditLogService;
$this->order = $order;
$this->serverService = $serverService;
$this->clusterService = $clusterService;
$this->user = $user;
}
public function log($resourceType = null, $resourceId = null, $action = null, $userId = null)
{
switch ($resourceType) {
case AuditLog::RESSOURCE_TYPE_ORDER:
$auditLog = new AuditLogFileTransferRequest();
$resource = $this->order->findOne($resourceId);
$auditLog->setFileTransferRequest($resource);
break;
case AuditLog::RESSOURCE_TYPE_SERVER:
$auditLog = new AuditLogServer();
$resource = $this->serverService->findOne($resourceId);
$auditLog->setServer($resource);
break;
case AuditLog::RESSOURCE_TYPE_CLUSTER:
$auditLog = new AuditLogCluster();
$resource = $this->clusterService->findOne($resourceId);
$auditLog->setCluster($resource);
break;
default:
$auditLog = new AuditLog();
}
$auditLog->setAction($action);
if ($userId) {
$user = new User();
$user->setId($userId);
$auditLog->setUser($user);
} elseif ($this->user) {
$auditLog->setUser($this->user);
}
$this->auditLogService->create($auditLog);
}
}