我正在将基于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会忽略这个属性以及如何保存它?
CODE
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;
}
}
答案 0 :(得分:0)
由于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);
}
}