Doctrine orm 条令忽略属性/列

Doctrine orm 条令忽略属性/列,doctrine-orm,doctrine,save,single-table-inheritance,Doctrine Orm,Doctrine,Save,Single Table Inheritance,我正在将基于Zend\Db的应用程序迁移到条令2 问题在于实体AuditLog。在迁移之前,它通过Zend\Db持久化(请参见代码块1) 现在,实体AuditLog有多个子类,它们一起实现模式(请参见代码块2) 既然我切换到了原则,我应该只需要创建一个适当的AuditLog对象,然后简单地持久化(…)它。实际上,这就是我正在尝试的(请参阅代码块3)。但是现在我发现AuditLog的属性$resourceId有一个问题:条令似乎忽略了它。该属性包含正确的值(我在调试器中看到),但是INSERT语句

我正在将基于
Zend\Db
的应用程序迁移到条令2

问题在于实体
AuditLog
。在迁移之前,它通过
Zend\Db
持久化(请参见代码块
1

现在,实体
AuditLog
有多个子类,它们一起实现模式(请参见代码块
2

既然我切换到了原则,我应该只需要创建一个适当的
AuditLog
对象,然后简单地
持久化(…)
它。实际上,这就是我正在尝试的(请参阅代码块
3
)。但是现在我发现
AuditLog
的属性
$resourceId
有一个问题:条令似乎忽略了它。该属性包含正确的值(我在调试器中看到),但是
INSERT
语句没有得到它,看起来像:

INSERT INTO audit_log
    (resource_id, action, datetime, user_id, resource_type)
VALUES
    (NULL, 'order.created', NULL, 1, 'order');
结果是:实体被持久化,但
资源\u id
为空

条令为何忽略这一属性以及如何保存它?


代码

1
使用
Zend\Db

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(...).
    }
}
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__);
    }
}
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 ...
}
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 ...
}
use Doctrine\ORM\Mapping as ORM;
/**
 * AuditLogServer
 *
 * @ORM\Table(name="audit_log")
 * @ORM\Entity
 */
class AuditLogServer extends AuditLog
{
    // ...
}
use Doctrine\ORM\Mapping as ORM;
/**
 * AuditLogCluster
 *
 * @ORM\Table(name="audit_log")
 * @ORM\Entity
 */
class AuditLogCluster extends AuditLog
{
    // ...
}
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(...).
    }
}
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;
    }
}
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(...).
    }
}
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__);
    }
}
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 ...
}
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 ...
}
use Doctrine\ORM\Mapping as ORM;
/**
 * AuditLogServer
 *
 * @ORM\Table(name="audit_log")
 * @ORM\Entity
 */
class AuditLogServer extends AuditLog
{
    // ...
}
use Doctrine\ORM\Mapping as ORM;
/**
 * AuditLogCluster
 *
 * @ORM\Table(name="audit_log")
 * @ORM\Entity
 */
class AuditLogCluster extends AuditLog
{
    // ...
}
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(...).
    }
}
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;
    }
}
AuditLogMapper

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(...).
    }
}
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__);
    }
}
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 ...
}
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 ...
}
use Doctrine\ORM\Mapping as ORM;
/**
 * AuditLogServer
 *
 * @ORM\Table(name="audit_log")
 * @ORM\Entity
 */
class AuditLogServer extends AuditLog
{
    // ...
}
use Doctrine\ORM\Mapping as ORM;
/**
 * AuditLogCluster
 *
 * @ORM\Table(name="audit_log")
 * @ORM\Entity
 */
class AuditLogCluster extends AuditLog
{
    // ...
}
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(...).
    }
}
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;
    }
}
2
实体

审核日志

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(...).
    }
}
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__);
    }
}
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 ...
}
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 ...
}
use Doctrine\ORM\Mapping as ORM;
/**
 * AuditLogServer
 *
 * @ORM\Table(name="audit_log")
 * @ORM\Entity
 */
class AuditLogServer extends AuditLog
{
    // ...
}
use Doctrine\ORM\Mapping as ORM;
/**
 * AuditLogCluster
 *
 * @ORM\Table(name="audit_log")
 * @ORM\Entity
 */
class AuditLogCluster extends AuditLog
{
    // ...
}
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(...).
    }
}
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;
    }
}
AuditLogOrder

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(...).
    }
}
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__);
    }
}
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 ...
}
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 ...
}
use Doctrine\ORM\Mapping as ORM;
/**
 * AuditLogServer
 *
 * @ORM\Table(name="audit_log")
 * @ORM\Entity
 */
class AuditLogServer extends AuditLog
{
    // ...
}
use Doctrine\ORM\Mapping as ORM;
/**
 * AuditLogCluster
 *
 * @ORM\Table(name="audit_log")
 * @ORM\Entity
 */
class AuditLogCluster extends AuditLog
{
    // ...
}
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(...).
    }
}
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;
    }
}
AuditLogServer

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(...).
    }
}
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__);
    }
}
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 ...
}
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 ...
}
use Doctrine\ORM\Mapping as ORM;
/**
 * AuditLogServer
 *
 * @ORM\Table(name="audit_log")
 * @ORM\Entity
 */
class AuditLogServer extends AuditLog
{
    // ...
}
use Doctrine\ORM\Mapping as ORM;
/**
 * AuditLogCluster
 *
 * @ORM\Table(name="audit_log")
 * @ORM\Entity
 */
class AuditLogCluster extends AuditLog
{
    // ...
}
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(...).
    }
}
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;
    }
}
AuditLogCluster

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(...).
    }
}
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__);
    }
}
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 ...
}
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 ...
}
use Doctrine\ORM\Mapping as ORM;
/**
 * AuditLogServer
 *
 * @ORM\Table(name="audit_log")
 * @ORM\Entity
 */
class AuditLogServer extends AuditLog
{
    // ...
}
use Doctrine\ORM\Mapping as ORM;
/**
 * AuditLogCluster
 *
 * @ORM\Table(name="audit_log")
 * @ORM\Entity
 */
class AuditLogCluster extends AuditLog
{
    // ...
}
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(...).
    }
}
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;
    }
}
3
坚持教条

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(...).
    }
}
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__);
    }
}
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 ...
}
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 ...
}
use Doctrine\ORM\Mapping as ORM;
/**
 * AuditLogServer
 *
 * @ORM\Table(name="audit_log")
 * @ORM\Entity
 */
class AuditLogServer extends AuditLog
{
    // ...
}
use Doctrine\ORM\Mapping as ORM;
/**
 * AuditLogCluster
 *
 * @ORM\Table(name="audit_log")
 * @ORM\Entity
 */
class AuditLogCluster extends AuditLog
{
    // ...
}
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(...).
    }
}
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;
    }
}
AuditLogMapper

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(...).
    }
}
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__);
    }
}
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 ...
}
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 ...
}
use Doctrine\ORM\Mapping as ORM;
/**
 * AuditLogServer
 *
 * @ORM\Table(name="audit_log")
 * @ORM\Entity
 */
class AuditLogServer extends AuditLog
{
    // ...
}
use Doctrine\ORM\Mapping as ORM;
/**
 * AuditLogCluster
 *
 * @ORM\Table(name="audit_log")
 * @ORM\Entity
 */
class AuditLogCluster extends AuditLog
{
    // ...
}
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(...).
    }
}
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\u id
用作
join列

/**
 * @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);
    }

}

由于
resource\u id
用作
join列

/**
 * @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);
    }

}