Declare Doctrine Embeddable as nullable or not
The problem having the logic inside the getter is that you can't access directly to property (if you do so you miss this specific behaviour)...
I was trying to solve this using a custom Hydrator but the problem was that doctrine does not allow to use custom hydrators when call to find(), findOneBy()...and the methods that do not use the queryBuilder.
Here is my solution:
- Imagine that we have an entity that looks like this:
<?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;
}
}
}
Then you need to create an EventListener in order to initialize this entity in the postLoad event:
<?php
use Doctrine\ORM\Event\LifecycleEventArgs;
class InitialiseDoctrineEntity
{
public function postLoad(LifecycleEventArgs $eventArgs): void
{
$entity = $eventArgs->getEntity();
if ($entity instanceof CanBeInitialized) {
$entity->initialize();
}
}
}
The great with this approach is that we can adapt the entities to our needs (not only to have nullable embeddables). For example: In Domain Driven Design, when we use the Hexagonal Architecture as a tactical approach to work with, we can initialize the Doctrine entities with all the changes needed to have our Domain entities as we want.
Does anybody know if I'm missing something
Nullable embeddables are not supported in Doctrine 2. They are expected to make it to version 3.
if there's a workaround
The solution "is to NOT use embeddables there, and [...] replace fields with embeddables [manually]" (@Ocramius)
Example:
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
);
}
}
(Snippet by Harrison Brown)