声明 Doctrine Embeddable 是否可为空
Declare Doctrine Embeddable as nullable or not
假设我有两个 Doctrine 实体,Person
和 Company
。两者都有一个接受地址值对象的 address
字段。根据业务规则,Company::Address
是必需的,而 Person::Address
可以为空。
Doctrine 2.5 提出 the Embeddable type,它显然是在考虑值对象的情况下构建的,事实上,我认为它是我案例的完美解决方案。
但是,有一件事我不能做:声明 Person::Address
可以为 null 而 Company::Address
不是。 Embeddable 的字段本身存在布尔值 nullable
属性,但这当然适用于地址嵌入的每个实体。
有谁知道我是否遗漏了什么,或者这是由于技术限制,是否有解决方法等?现在我看到的唯一解决方案是将所有可嵌入字段声明为 nullable: true
并在我的代码中处理约束。
Does anybody know if I'm missing something
Doctrine 2 不支持可为空的可嵌入对象。它们有望进入版本 3。
if there's a workaround
解决方案"is to NOT use embeddables there, and [...] replace fields with embeddables [manually]" (@Ocramius)
示例:
class Product
{
private $sale_price_amount;
private $sale_price_currency;
public function getSalePrice(): ?SalePrice
{
if (is_null($this->sale_price_currency)
|| is_null($this->sale_price_amount)
) {
return null;
}
return new SalePrice(
$this->sale_price_currency,
$this->sale_price_amount
);
}
}
(Harrison Brown 的片段)
getter 中的逻辑问题是您无法直接访问 属性(如果您这样做,您会错过此特定行为)...
我试图使用自定义 Hydrator 来解决这个问题,但问题是当调用 find()、findOneBy()...以及不使用 queryBuilder 的方法时,学说不允许使用自定义 hydrator .
这是我的解决方案:
- 假设我们有一个如下所示的实体:
<?php
interface CanBeInitialized
{
public function initialize(): void;
}
class Address
{
private $name;
public function name(): string
{
return $this->name;
}
}
class User implements CanBeInitialized
{
private $address;
public function address(): ?Address
{
return $this->address;
}
public function initialize(): void
{
$this->initializeAddress();
}
private function initializeAddress(): void
{
$addressNameProperty = (new \ReflectionClass($this->address))->getProperty('value');
$addressNameProperty->setAccessible(true);
$addressName = $addressNameProperty->getValue($this->address);
if ($addressName === null) {
$this->address = null;
}
}
}
然后您需要创建一个 EventListener 以便在 postLoad 事件中初始化此实体:
<?php
use Doctrine\ORM\Event\LifecycleEventArgs;
class InitialiseDoctrineEntity
{
public function postLoad(LifecycleEventArgs $eventArgs): void
{
$entity = $eventArgs->getEntity();
if ($entity instanceof CanBeInitialized) {
$entity->initialize();
}
}
}
这种方法的伟大之处在于我们可以根据我们的需要调整实体(不仅具有可为空的嵌入项)。例如:在领域驱动设计中,当我们使用六边形架构作为一种战术方法来使用时,我们可以使用我们想要的领域实体所需的所有更改来初始化 Doctrine 实体。
假设我有两个 Doctrine 实体,Person
和 Company
。两者都有一个接受地址值对象的 address
字段。根据业务规则,Company::Address
是必需的,而 Person::Address
可以为空。
Doctrine 2.5 提出 the Embeddable type,它显然是在考虑值对象的情况下构建的,事实上,我认为它是我案例的完美解决方案。
但是,有一件事我不能做:声明 Person::Address
可以为 null 而 Company::Address
不是。 Embeddable 的字段本身存在布尔值 nullable
属性,但这当然适用于地址嵌入的每个实体。
有谁知道我是否遗漏了什么,或者这是由于技术限制,是否有解决方法等?现在我看到的唯一解决方案是将所有可嵌入字段声明为 nullable: true
并在我的代码中处理约束。
Does anybody know if I'm missing something
Doctrine 2 不支持可为空的可嵌入对象。它们有望进入版本 3。
if there's a workaround
解决方案"is to NOT use embeddables there, and [...] replace fields with embeddables [manually]" (@Ocramius)
示例:
class Product
{
private $sale_price_amount;
private $sale_price_currency;
public function getSalePrice(): ?SalePrice
{
if (is_null($this->sale_price_currency)
|| is_null($this->sale_price_amount)
) {
return null;
}
return new SalePrice(
$this->sale_price_currency,
$this->sale_price_amount
);
}
}
(Harrison Brown 的片段)
getter 中的逻辑问题是您无法直接访问 属性(如果您这样做,您会错过此特定行为)...
我试图使用自定义 Hydrator 来解决这个问题,但问题是当调用 find()、findOneBy()...以及不使用 queryBuilder 的方法时,学说不允许使用自定义 hydrator .
这是我的解决方案:
- 假设我们有一个如下所示的实体:
<?php
interface CanBeInitialized
{
public function initialize(): void;
}
class Address
{
private $name;
public function name(): string
{
return $this->name;
}
}
class User implements CanBeInitialized
{
private $address;
public function address(): ?Address
{
return $this->address;
}
public function initialize(): void
{
$this->initializeAddress();
}
private function initializeAddress(): void
{
$addressNameProperty = (new \ReflectionClass($this->address))->getProperty('value');
$addressNameProperty->setAccessible(true);
$addressName = $addressNameProperty->getValue($this->address);
if ($addressName === null) {
$this->address = null;
}
}
}
然后您需要创建一个 EventListener 以便在 postLoad 事件中初始化此实体:
<?php
use Doctrine\ORM\Event\LifecycleEventArgs;
class InitialiseDoctrineEntity
{
public function postLoad(LifecycleEventArgs $eventArgs): void
{
$entity = $eventArgs->getEntity();
if ($entity instanceof CanBeInitialized) {
$entity->initialize();
}
}
}
这种方法的伟大之处在于我们可以根据我们的需要调整实体(不仅具有可为空的嵌入项)。例如:在领域驱动设计中,当我们使用六边形架构作为一种战术方法来使用时,我们可以使用我们想要的领域实体所需的所有更改来初始化 Doctrine 实体。