假设我有两个教义实体,
Person
和Company
。两者都有一个 address
字段,它接受 Address 值对象。根据业务规则,Company::Address
为必填项,而Person::Address
可以为空。
Doctrine 2.5 提出了 Embeddable 类型,它显然是在考虑值对象的情况下构建的,事实上,我认为它是适合我的情况的完美解决方案。
但是,有一件事我不能做:声明
Person::Address
可以为空,而 Company::Address
则不能。可嵌入字段本身存在一个布尔值 nullable
属性,但这当然适用于嵌入地址的每个实体。
有人知道我是否遗漏了什么,或者这是由于技术限制,是否有解决方法等?现在我看到的唯一解决方案是将所有可嵌入字段声明为
nullable: true
并处理我的代码中的约束。
有人知道我是否遗漏了什么吗
Doctrine 2 不支持可空嵌入。预计它们将进入版本 3。
如果有解决方法
解决方案“是不要在那里使用可嵌入项,并且[...][手动]用可嵌入项替换字段”(@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
);
}
}
(哈里森·布朗的片段)
getter 内部逻辑的问题是你无法直接访问属性(如果你这样做,你就会错过这个特定的行为)...
我试图使用自定义水化器来解决这个问题,但问题是教义不允许在调用 find()、findOneBy()...以及不使用 queryBuilder 的方法时使用自定义水化器。
这是我的解决方案:
<?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 中,使用嵌入对象时,通常不能将嵌入对象本身声明为可为空或不可为空。但是,您可以控制使用可嵌入的实体内嵌入字段的可为空性。
以下是处理嵌入字段的可为空性的方法:
可为空的嵌入字段:如果嵌入中的某些字段应该为空,您可以在嵌入类本身中将它们标记为可为空。
// AddressEmbeddable.php
class AddressEmbeddable
{
/** @ORM\Column(type="string", nullable=true) */
private $street;
/** @ORM\Column(type="string", nullable=true) */
private $city;
// Getter and setter methods
}
特定于实体的可为空性:在实体类(例如,个人和公司)中,您可以配置嵌入字段的可为空性。
// Person.php
class Person
{
/**
* @ORM\Embedded(class="AddressEmbeddable")
* @ORM\Column(nullable=true)
*/
private $address;
}
// Company.php
class Company
{
/**
* @ORM\Embedded(class="AddressEmbeddable")
* @ORM\Column(nullable=false)
*/
private $address;
}
在此示例中,Person 实体的
$address
属性可为 null,因为 @ORM\Column(nullable=true)
注释应用于嵌入字段。相反,Company 实体的 $address
属性不可为 null,因为它被指定为 nullable=false
。
通过在实体级别配置可空性,您可以控制使用可嵌入的每个实体的嵌入字段是否可为空。