声明Doctrine Embeddable是否可以为空

时间:2017-06-19 11:46:57

标签: php doctrine-orm orm doctrine value-objects

假设我有两个Doctrine实体,PersonCompany。两者都有一个address字段,它接受一个Address值对象。根据业务规则,Company::Address是必需的,而Person::Address可以为空。

Doctrine 2.5提出了the Embeddable type,它显然是在考虑价值对象的情况下构建的,事实上,我认为它是我案例的完美解决方案。

但是,有一件事我不能做:声明Person::Address可以为空,而Company::Address则不可以。 Embeddable的字段本身存在布尔nullable属性,但当然这适用于嵌入地址的每个实体。

有人知道我是否遗漏了某些东西,或者这是否是由于技术限制,是否有解决方法等?现在,我看到的唯一解决方案是将所有Embeddable字段声明为nullable: true并在我的代码中处理约束。

2 个答案:

答案 0 :(得分:6)

  

有人知道我是否遗漏了某些东西

Doctrine 2不支持Nullable embeddables。预计它们将用于版本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
        );
    }
}

Harrison Brown的摘录)

答案 1 :(得分:2)

在getter中存在逻辑的问题是您无法直接访问属性(如果这样做,则会错过此特定行为)...

我试图使用自定义Hydrator解决此问题,但问题是当调用find(),findOneBy()...以及不使用queryBuilder的方法时,该学说不允许使用自定义水化器。< / p>

这是我的解决方法:

  1. 想象一下,我们有一个看起来像这样的实体:
<?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实体,以根据需要拥有域实体。