如何通过非主键引用 Doctrine 2 中的多对一关联
How to reference many-to-one association in Doctrine 2 by a non-primary key
据我了解,Doctrine 需要多对一 关联以在一侧引用主键。 (参见 here or here)
如何解决由 Doctrine 2 中的唯一键引用的多对一关联?
在下文中,我将展示到目前为止我所提出的内容以及 运行 对上述问题的看法。必须进行哪些更改才能使其在我的环境中工作?
就我而言:
项目具有 snapshotId
和 cid
等属性。 cid
表示内部项目编号。在某些更改中,将拍摄快照并使用唯一的 snapshotId
保存(包括关联的 table 中的所有相关条目 - 此处未显示)。具有空 (null) snapshotId 的条目是当前状态。
正在 ProjectHistory
中跟踪项目的更改。出于演示目的,该实体具有属性 function
、modifyDate
和 modifyUserId
.
该项目与 ProjectHistory 具有一对多关联。 ProjectHistory 与 Project 的多对一关联。前者按预期工作,后者带来开头提到的错误。
<?php
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity()
* @ORM\Table(
* indexes={
* @ORM\Index(
* name="cid",
* columns={"cid"}
* ),
* },
* uniqueConstraints={
* @ORM\UniqueConstraint(
* name="unique_snapshot_per_project",
* columns={"snapshot_id", "cid"}
* ),
* },
* )
*/
class Project
{
/**
* @ORM\Id()
* @ORM\Column(type="guid")
* @ORM\GeneratedValue(strategy="UUID")
* @var string
*/
private $id;
/**
* @ORM\Column(type="guid", unique=true, nullable=true)
* @var string|null
*/
private $snapshotId;
/**
* @ORM\Column()
* @var string
*/
private $cid;
/**
* @ORM\OneToMany(targetEntity="ProjectHistory", mappedBy="projectCid", cascade={"persist"})
* @var Collection|ProjectHistory[]
*/
private $projectHistories;
public function __construct()
{
$this->projectHistories = new ArrayCollection();
}
public function toArray(): array
{
return [
'id' => $this->getId(),
'snapshotId' => $this->getSnapshotId(),
'cid' => $this->getCid(),
'projectHistories' => $this->getProjectHistoriesAsArray(),
];
}
public function getProjectHistoriesAsArray(): array
{
$projectHistories = [];
foreach ($this->getProjectHistories() as $projectHistory) {
$projectHistories[] = $projectHistory->toArray();
}
return $projectHistories;
}
public function getId(): ?string
{
return $this->id;
}
public function setId(string $id): void
{
$this->id = $id;
}
public function getSnapshotId(): ?string
{
return $this->snapshotId;
}
public function setSnapshotId(string $snapshotId): void
{
$this->snapshotId = $snapshotId;
}
public function getCid(): string
{
return $this->cid;
}
public function setCid(string $cid): void
{
$this->cid = $cid;
}
public function getProjectHistories(): Collection
{
return $this->projectHistories;
}
/**
* @param ArrayCollection|ProjectHistory[] $projectHistories
* @return Project
*/
public function addProjectHistories(ArrayCollection $projectHistories): Project
{
foreach ($projectHistories as $projectHistory) {
if (! $this->projectHistories->contains($projectHistory)) {
$this->projectHistories->add($projectHistory);
$projectHistory->setProject($this);
}
}
return $this;
}
/**
* @param ArrayCollection|ProjectHistory[] $projectHistories
* @return Project
*/
public function removeProjectHistories(ArrayCollection $projectHistories): Project
{
foreach ($projectHistories as $projectHistory) {
if ($this->projectHistories->contains($projectHistory)) {
$this->projectHistories->remove($projectHistory);
}
}
return $this;
}
}
<?php
namespace App\Entity;
use DateTime;
use DateTimeInterface;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity()
*/
class ProjectHistory
{
/**
* @ORM\Id()
* @ORM\Column(type="guid")
* @ORM\GeneratedValue(strategy="UUID")
* @var string
*/
private $id;
/**
* @ORM\Column()
* @var string
*/
private $function;
/**
* @ORM\Column(type="datetime")
* @var Datetime
*/
private $modifyDate;
/**
* @ORM\Column(type="guid")
* @var string
*/
private $modifyUserId;
/**
* @ORM\ManyToOne(targetEntity="Project", inversedBy="projectHistories")
* @ORM\JoinColumn(name="project_cid", referencedColumnName="cid")
* @var Project
*/
private $project;
public function toArray(): array
{
return [
'id' => $this->getId(),
'projectCid' => $this->getProjectCid(),
'function' => $this->getFunction(),
'modifyDate' => $this->getModifyDateAsString(),
'modifyUserId' => $this->getModifyUserId(),
];
}
public function getModifyDateAsString(): string
{
if (null === $this->getModifyDate()) {
return '';
}
return $this->getModifyDate()->format(DateTimeInterface::ATOM);
}
public function getProjectCid(): ?string
{
if (null !== $this->getProject()) {
return $this->getProject()->getCid();
}
return null;
}
public function getId(): ?string
{
return $this->id;
}
public function setId(string $id): void
{
$this->id = $id;
}
public function getFunction(): string
{
return $this->function;
}
public function setFunction($function): void
{
$this->function = $function;
}
public function getModifyDate(): DateTime
{
return $this->modifyDate;
}
public function setModifyDate(DateTime $modifyDate): void
{
$this->modifyDate = $modifyDate;
}
public function getModifyUserId(): string
{
return $this->modifyUserId;
}
public function setModifyUserId(string $modifyUserId): void
{
$this->modifyUserId = $modifyUserId;
}
public function getProject(): ?Project
{
return $this->project;
}
public function setProject(Project $project): void
{
$this->project = $project;
}
}
非常感谢任何帮助。如果您需要更多信息,请告诉我。
通常如果你需要做这样的事情,我的意思是通过非主键引用,你的设计有问题。阅读您的问题时,我首先想到的是 Event Soursing. There is good implimentation of ES for PHP - prooph。不幸的是,将现有项目重构为 ES 并非易事,但也许它可以帮助您更好地理解历史变更和快照的概念。
按照目前的情况,我可以说Project
不是做快照的目的。您可以修改模型并创建与 Project
相关的 ProjectSnapshot
实体,然后 ProjectHistory
将与 ProjectSnapshot
相关。此外,我不建议您在快照中存储任何关联(和外键),而是只保存相关实体的标识符。注意loggable extension,也许会有用。
据我了解,Doctrine 需要多对一 关联以在一侧引用主键。 (参见 here or here)
如何解决由 Doctrine 2 中的唯一键引用的多对一关联?
在下文中,我将展示到目前为止我所提出的内容以及 运行 对上述问题的看法。必须进行哪些更改才能使其在我的环境中工作?
就我而言:
项目具有 snapshotId
和 cid
等属性。 cid
表示内部项目编号。在某些更改中,将拍摄快照并使用唯一的 snapshotId
保存(包括关联的 table 中的所有相关条目 - 此处未显示)。具有空 (null) snapshotId 的条目是当前状态。
正在 ProjectHistory
中跟踪项目的更改。出于演示目的,该实体具有属性 function
、modifyDate
和 modifyUserId
.
该项目与 ProjectHistory 具有一对多关联。 ProjectHistory 与 Project 的多对一关联。前者按预期工作,后者带来开头提到的错误。
<?php
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity()
* @ORM\Table(
* indexes={
* @ORM\Index(
* name="cid",
* columns={"cid"}
* ),
* },
* uniqueConstraints={
* @ORM\UniqueConstraint(
* name="unique_snapshot_per_project",
* columns={"snapshot_id", "cid"}
* ),
* },
* )
*/
class Project
{
/**
* @ORM\Id()
* @ORM\Column(type="guid")
* @ORM\GeneratedValue(strategy="UUID")
* @var string
*/
private $id;
/**
* @ORM\Column(type="guid", unique=true, nullable=true)
* @var string|null
*/
private $snapshotId;
/**
* @ORM\Column()
* @var string
*/
private $cid;
/**
* @ORM\OneToMany(targetEntity="ProjectHistory", mappedBy="projectCid", cascade={"persist"})
* @var Collection|ProjectHistory[]
*/
private $projectHistories;
public function __construct()
{
$this->projectHistories = new ArrayCollection();
}
public function toArray(): array
{
return [
'id' => $this->getId(),
'snapshotId' => $this->getSnapshotId(),
'cid' => $this->getCid(),
'projectHistories' => $this->getProjectHistoriesAsArray(),
];
}
public function getProjectHistoriesAsArray(): array
{
$projectHistories = [];
foreach ($this->getProjectHistories() as $projectHistory) {
$projectHistories[] = $projectHistory->toArray();
}
return $projectHistories;
}
public function getId(): ?string
{
return $this->id;
}
public function setId(string $id): void
{
$this->id = $id;
}
public function getSnapshotId(): ?string
{
return $this->snapshotId;
}
public function setSnapshotId(string $snapshotId): void
{
$this->snapshotId = $snapshotId;
}
public function getCid(): string
{
return $this->cid;
}
public function setCid(string $cid): void
{
$this->cid = $cid;
}
public function getProjectHistories(): Collection
{
return $this->projectHistories;
}
/**
* @param ArrayCollection|ProjectHistory[] $projectHistories
* @return Project
*/
public function addProjectHistories(ArrayCollection $projectHistories): Project
{
foreach ($projectHistories as $projectHistory) {
if (! $this->projectHistories->contains($projectHistory)) {
$this->projectHistories->add($projectHistory);
$projectHistory->setProject($this);
}
}
return $this;
}
/**
* @param ArrayCollection|ProjectHistory[] $projectHistories
* @return Project
*/
public function removeProjectHistories(ArrayCollection $projectHistories): Project
{
foreach ($projectHistories as $projectHistory) {
if ($this->projectHistories->contains($projectHistory)) {
$this->projectHistories->remove($projectHistory);
}
}
return $this;
}
}
<?php
namespace App\Entity;
use DateTime;
use DateTimeInterface;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity()
*/
class ProjectHistory
{
/**
* @ORM\Id()
* @ORM\Column(type="guid")
* @ORM\GeneratedValue(strategy="UUID")
* @var string
*/
private $id;
/**
* @ORM\Column()
* @var string
*/
private $function;
/**
* @ORM\Column(type="datetime")
* @var Datetime
*/
private $modifyDate;
/**
* @ORM\Column(type="guid")
* @var string
*/
private $modifyUserId;
/**
* @ORM\ManyToOne(targetEntity="Project", inversedBy="projectHistories")
* @ORM\JoinColumn(name="project_cid", referencedColumnName="cid")
* @var Project
*/
private $project;
public function toArray(): array
{
return [
'id' => $this->getId(),
'projectCid' => $this->getProjectCid(),
'function' => $this->getFunction(),
'modifyDate' => $this->getModifyDateAsString(),
'modifyUserId' => $this->getModifyUserId(),
];
}
public function getModifyDateAsString(): string
{
if (null === $this->getModifyDate()) {
return '';
}
return $this->getModifyDate()->format(DateTimeInterface::ATOM);
}
public function getProjectCid(): ?string
{
if (null !== $this->getProject()) {
return $this->getProject()->getCid();
}
return null;
}
public function getId(): ?string
{
return $this->id;
}
public function setId(string $id): void
{
$this->id = $id;
}
public function getFunction(): string
{
return $this->function;
}
public function setFunction($function): void
{
$this->function = $function;
}
public function getModifyDate(): DateTime
{
return $this->modifyDate;
}
public function setModifyDate(DateTime $modifyDate): void
{
$this->modifyDate = $modifyDate;
}
public function getModifyUserId(): string
{
return $this->modifyUserId;
}
public function setModifyUserId(string $modifyUserId): void
{
$this->modifyUserId = $modifyUserId;
}
public function getProject(): ?Project
{
return $this->project;
}
public function setProject(Project $project): void
{
$this->project = $project;
}
}
非常感谢任何帮助。如果您需要更多信息,请告诉我。
通常如果你需要做这样的事情,我的意思是通过非主键引用,你的设计有问题。阅读您的问题时,我首先想到的是 Event Soursing. There is good implimentation of ES for PHP - prooph。不幸的是,将现有项目重构为 ES 并非易事,但也许它可以帮助您更好地理解历史变更和快照的概念。
按照目前的情况,我可以说Project
不是做快照的目的。您可以修改模型并创建与 Project
相关的 ProjectSnapshot
实体,然后 ProjectHistory
将与 ProjectSnapshot
相关。此外,我不建议您在快照中存储任何关联(和外键),而是只保存相关实体的标识符。注意loggable extension,也许会有用。