Embedded Value with Doctrine <= 2.4.*

If you're still stuck in Doctrine 2.4, you may wonder what an acceptable solution for using Embedded Values with Doctrine < 2.5 is. We need to now surrogate all the Value Object attributes in the Product Entity, which means creating new artificial attributes that will hold the information of the Value Object. With this in place, we can map all those new attributes using Doctrine. Let's see what impact this has on the Product Entity:

class Product 
{
private $productId; 
private $
name;
private $price;
private $surrogateCurrencyIsoCode;
private $surrogateAmount;

public function __construct($aProductId, $aName, Money $aPrice)
{
$this->setProductId($aProductId);
$this->setName($aName);
$this->setPrice($aPrice);
}

private function setPrice(Money $aMoney)
{
$this->price = $aMoney;
$this->surrogateAmount = $aMoney->amount();
$this->surrogateCurrencyIsoCode =
$aMoney->currency()->isoCode();
}

private function price()
{
if (null === $this->price) {
$this->price = new Money(
$this->surrogateAmount,
new Currency($this->surrogateCurrency)
);
}
return $this->price;
}

// ...
}

As you can see, there are two new attributes: one for the amount, and another for the ISO code of the currency. We've also updated the setPrice method in order to keep attribute consistency when setting it. On top of this, we updated the price getter in order to return the Money Value Object built from the new fields. Let's see how the corresponding XML Doctrine mapping should be changed:

 <?xml version="1.0" encoding="utf-8"?>
<doctrine-mapping
xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://doctrine-project.org/schemas/orm/doctrine-mapping
https://raw.github.com/doctrine/doctrine2/master/doctrine-mapping.xsd">

<entity
name="Product"
table="product">

<id
name="id"
column="id"
type="string"
length="255" >
<generator strategy="NONE">
</generator>
</id>

<field
name="name"
type="string"
length="255"
/>

<field
name="surrogateAmount"
type="integer"
column="price_amount"
/>
<field
name="surrogateCurrencyIsoCode"
type="string"
column="price_currency"
/>
</entity>
</doctrine-mapping>
Surrogate Attributes
These two new fields don't strictly belong to the Domain, as they don't refer to Infrastructure details. Rather, they're a necessity due to the lack of embeddable support in Doctrine. There are alternatives that can push these two attributes outside the pure Domain; however, this approach is simpler, easier, and, as a tradeoff, acceptable. There's another use of surrogate attributes in this book; you can find it in  the sub-section Surrogate Identity of the section Identity Operation of Chapter 4, Entities.

If we wanted to push these two attributes outside of the Domain, this could be achieved through the use of an Abstract Factory. First, we need to create a new Entity, DoctrineProduct, in our Infrastructure folder. This Entity will extend from Product Entity. All surrogate fields will be placed in the new class, and methods such as price or setPrice should be reimplemented. We'll map Doctrine to use the new DoctrineProduct as opposed to the Product Entity.

Now we're able to fetch Entities from the database, but what about creating a new Product? At some point, we're required to call new Product, but because we need to deal with DoctrineProduct and we don't want our Application Services to know about Infrastructure details, we'll need to use Factories to create Product Entities. So, in every instance where Entity creation occurs with new, you'll instead call createProduct on ProductFactory.

There could be many additional classes required to avoid placing the surrogate attributes in the original Entity. As such, it's our recommendation to surrogate all the Value Objects to the same Entity, though this admittedly leads to a less pure solution.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset