[DDC-93] It would be nice if we could have support for ValueObjects Created: 01/Nov/09  Updated: 14/Apr/13

Status: Open
Project: Doctrine 2 - ORM
Component/s: ORM
Affects Version/s: None
Fix Version/s: 2.x
Security Level: All

Type: New Feature Priority: Major
Reporter: Avi Block Assignee: Guilherme Blanco
Resolution: Unresolved Votes: 37
Labels: None

Issue Links:
Duplicate
is duplicated by DDC-648 Custom mapping types for multiple DB ... Resolved
Reference
is referenced by DDC-2374 [GH-634] [WIP] Value objects Open

 Description   
class User {
	/**
	 * @Column(type="string")
	 */
	private $address;
	
	/**
	 * @Column(type="string")
	 */
	private $city;
	
	/**
	 * @Column(type="string")
	 */
	private $state;
}

We could have:

class User {
	/**
	 * @Component(class="Address")
	 */
	 private $address;
}

It would my life a lot easier....


Notes for implementation

Value objects can come in two forms:

a) as embedded value objects
b) as collections of value objects

An implementation should concentrate on a) first. The following things all concentrate on a).

DQL Support

Conditions:

1. "select f from Foo f where f.embedded.value = ?1" (setParameter(1, $scalarValue))
2. "select f from Foo f where f.embedded = ?1" (setParameter(1, $embeddedValueObject))

At least Nr.1 must be possible in a first implementation.

Selecting:

1. "select f from Foo f" must explode embedded value objects in the SQL SELECT clause.
2. "select f.embedded from Foo f" must explode the columns of the embedded object in the SQL SELECT clause.

At least Nr. 1 must be possible in a first implementation, obviously.

Components affected (among others): Parser, SqlWalker, ...

Persisters

The persisters need to take embedded value objects into account when persisting as well as loading entities.

Components affected (among others): Persisters, UnitOfWork, ...

Metadata

ClassMetadataInfo needs to be extended with a field (probably an array) that contains the mappings of embedded values.
New annotations as well as XML/YAML elements are needed.

Components affected (among others): ClassMetadataInfo, AnnotationDriver, YamlDriver, XmlDriver, doctrine-mapping.xsd, ...

Change Tracking

If value objects are supposed to be immutable this is easy and might require no or few changes. If, however, we want to track changes in mutable value objects it might get more complicated.

Components affected (among others): UnitOfWork, ...



 Comments   
Comment by Benjamin Eberlei [ 05/Nov/09 ]

formated snippets nicely

Comment by Andrea Turso [ 09/Dec/09 ]

I need this feature too.

But I would suggest using the same annotation used by JPA

@Embeddable

+1

Comment by Alan Gabriel Bem [ 17/Dec/09 ]

You should also take into consideration different storage strategies of ValueObjects.

Martin Fowler points out - in „PoEAA" - two approaches: Embedded Value (which is the one presented above) and Serialized LOB .
Both have their pros and cons, that's why Doctrine2 should give developers choice of selecting the fittest solution.

Comment by Avi Block [ 17/Dec/09 ]

Of course technically we can similate a serialized LOB with a new Doctrine 2 type.

Comment by Alan Gabriel Bem [ 17/Dec/09 ]

I don't like that idea - Its so not generic.

VO as a pattern is important building block of domain model, which clearly indicates that VO as a feature of Doctrine2 should be tailor-made.


To anyone of dev-team reading this issue: without VOs Doctrine is not yet DDD-ready, please hurry

Comment by Roman S. Borschel [ 18/Dec/09 ]

Serialized LOB is not very useful IMHO and has lots of problems (many mentioned in PoEEA already).

@Alan: I appreciate your nice reminder and I'm sure you mean it in a friendly way, but please keep in mind that noone is paid to work on this project. It all happens in free/spare time and the current state of the project already consumed at least 1 1/2 years spending many hours weekly on this project from me alone. Not to speak of the others.

Thus, there is no point in demanding something or telling us to hurry. The best way to get a feature in is to provide a (good) patch that we find worth including.

I started to add notes to this issue to collect all the things that need to be done for this feature.

In the meantime, its not too hard/ugly to get a half-way decent embedded value yourself:

/** @Entity @HasLifecycleCallbacks */
class Foo {
    // annotations not shown
    private $id;
    private $embedded;
    private $value1; // never reveal to public
    private $value2; // never reveal to public
    private $value3; // never reveal to public

   public function getEmbedded() {
       return $this->embedded;
   }

   public function setEmbedded($embedded) {
       $this->embedded = $embedded;
   }
   
   /** @PrePersist @PreUpdate */
   function _destructEmbedded() {
       // destruct $embedded into $value1, $value2, $value3
   }

   /** @PostLoad */
   function _constructEmbedded() {
      // construct $embedded from $value1, $value2, $value3 
   }
}

Several variations of this are possible, also with an external event listener instead of callbacks but in that case you might need to use reflection to get at the values.

Comment by Alan Gabriel Bem [ 25/Dec/09 ]

I want to share my thoughts on possible VOs collections implementations.

1. As it was mentioned earlier serialized (C)LOB is one solution. Implementation of storing/retrieving object graphs alone is quite simple, but it's complex in terms of SELECTs with conditions.
Composing SQL condition would result in some nasty constructions e.g. vo_collection_column LIKE '%foo%bar%' which output format would depend on serialization target (CSV, XML, YAML, PHP serialized objects etc.). Also in most cases it would be impossible to obtain eligible result.

I'm not taking Regexp or XPath operators into consideration as only few RDBMS support them.

2. The second solution is to break VOs graph into separate related table... or tables if we consider that VO can contain another VO(s). It's not so fast as serialized LOB but more flexible and it utilize power of RDMS,
But there is one catch: Doctrine2 must preserve nature of VO. To make it happen during Entities persisting - if any change in dependant VOs graph has been made - all associated VOs rows in database should be deleted and the new/changed VOs graph should be inserted in their place.
I know it could be inefficient while dealing with large object graphs, yet faster than comparing VOs one-by-one.

In conclusion:
serialized LOB is extremely fast in CRUD-like operations on aggregates, however very search unfriendly.
Separate ValueObjects tables are better where serialized LOB lacks, but slower in exploitation.

I can't tell which approach is superior, because each of them is valid under different circumstances.

Hope this helps.

@Alan: I appreciate your nice reminder and I'm sure you mean it in a friendly way [...]

Of course I do.

Comment by Benjamin Eberlei [ 13/Mar/10 ]

It would be easy to implement value objects in userland using the XML capabilities of many RDBMS:

1. Implement an Xpath function on the Dql Parser
2. Implement a User-Defined Type for each value object that handles the translation from and to XML.

The second point can be heavily optimized when value objects are immutable with an own identiy map of value types inside the Type flyweight instance.

Comment by Avi Block [ 13/Mar/10 ]

I more or less suggested something similar above.

Comment by Benjamin Eberlei [ 14/Mar/10 ]

ah, my bad - i must have overseen this

Comment by John Kleijn [ 16/May/10 ]

+1

This would be awesome.

Comment by Matthias Pigulla [ 09/Nov/10 ]

Don't forget (especially with regard to SLOBs) that values might in turn contain references to Entities.

Example: An "Order" might be an @Entity and might have a field (an array) of OrderLineItems as value. Each OrderLineItem might e. g. carry quantity or disconunt and references a Product (@Entity).

So even if you don't need the traversal from Product to all the Orders it is contained in, serializing the OrderLineItems needs a way to "cut off" the object graph at the transition towards the Product but must place some kind of referral there so that upon unserialization (of the OrderLineItem list, that is, during Order load) the Product references in every OrderLineItem are at least initialized with proxies again.

Don't know whether/how referential integrity (OrderLineItems <-> Products) would make sense or could be implemented here.

Comment by Benjamin Eberlei [ 24/Dec/10 ]

Pushed back to 2.x, this feature is probably the largest feature request we have and we'd rather focus on small improvements for 2.1

Comment by Nino Martincevic [ 11/Jan/11 ]

Several thinks to consider/not to oversee here:

1) There are value objects with identity. I know that is not DDD-conform but only at first sight. It means they are technically entities but are treated like VOs.
Common examples are Zipcode or country. As they have identity (e.g. Zipcode: de-40723) they are entities but are created and interchanged like normal VOs.
On the google DDD-List they were often referenced aS Lookup Entities.

2) In virtually all (business) cases a collection of VO is an Entity. How else could you reference (add or remove) single elements of that list?
There are exceptions here like a undefinded number of VOs in a collection, but in that case you can only add or remove a quantity of it.
As a true collection (say 3 addresses for a client = Entity ClientAdresses) you would have to give them some kind of identity, even if it is only having
a sequential number in that collection.

@Matthias: OrderLineItems is an example of actually being an Entity.

Comment by Ondrej Sibrina [ 03/Jun/11 ]

Hi guys. I face this in my own way. Hope you won't wake up your neighbours with loud laugh.

Every @Entity extends my BaseEntity object which provide kind of wrap for value with ValueBase object. So when want to get/set value from entity you call $entity->getData() where you won't get value "data" but wrapping ValueBase for value "data". Then you can get bare value by getValue(). Name of value class is in annotation and would be child of ValueBase.

There's also parent class Base for EntityBase and ValueBase. In my case class Base is something like HTML element. So in the end you can use $entity->renderHtml() or $value->renderHtml() no matter if you're rendering value or @Entity. There's more features like validation, filtering and hydration value/entity from HTML forms, but it's extra.

Implementation:

"Base.php"
 
 /* @MappedSuperclass */
abstract class Base {
  /* there're methods like _getParent(), _getPropertyName(), etc. used in code behind */
}
"ValueBase.php"
 
abstract class ValueBase extends Base { 
   public function getValue() {
        return $this->_getParent()->{$this->_getPropertyName()};
   }
   
   public function setValue($value) {
        $this->_getParent()->{$this->_getPropertyName()} = $value;
   }
}
"EntityBase.php"
 
/** @MappedSuperclass */
abstract class EntityBase extends Base {
    public function __call($name, $arguments) {
        /* get property object */
        $pattern = '/^get(.*)$/u';
        preg_match($pattern, $name, $matches);
        if (isset($matches[1])) {
            $propertyName = lcfirst($matches[1]);
            return $this->get($propertyName);
        }

        /* set entity */
        $pattern = '/^set(.*)$/u';
        preg_match($pattern, $name, $matches);
        if (isset($matches[1])) {
            $propertyName = lcfirst($matches[1]);
            return $this->set($propertyName, $arguments[0]);
        }
    }

    public function get($propertyName) {
	    $property = $this->_getElementProperty($propertyName);

	    if ($property == null)
		throw new Exception(sprintf("There isn't property like '%s'.", $propertyName));

	    /* for collections and entities */
	    if ($property["type"] == "collection" || $property["type"] == "entity") {
		$element = $this->{$propertyName};
		if ($element != null) {
		    $element->_setParent($this);
		    $element->_setPropertyName($propertyName);
		} elseif ($property["type"] == "entity") {
		    $element = new $property["class"];
		    $element->_setParent($this);
		    $element->_setPropertyName($propertyName);
		    $element->_setNullEntity();
		    $this->{$propertyName} = $element;
		}
		return $element;
	    }
	    else {
	    /* for values */
		if (!isset($this->_loadedEntities[$propertyName])) {
		    $this->_loadedEntities[$propertyName] = new $property["class"]($this, $propertyName);
		}
		return $this->_loadedEntities[$propertyName];
	    }
    }

    public function set($propertyName, $value) {
        $property = $this->_getElementProperty($propertyName);

        if ($property == null)
            throw new Exception(sprintf("There isn't property like '%s'.", $propertyName));

        /* for collections and entities */
        if ($property["type"] == "collection" || $property["type"] == "entity") {
            $this->{$propertyName} = $value;
        }
        /* for values */ else {
            throw new Exception(sprintf("Can't call set on value property '%s'.", $propertyName));
        }

        return $this;
    }
}

Note that there's something i call "NullEntity". Instead of getting bare "null" you'll get @Entity child of EntityBase, where is set property nullEntity. Then there's posibility to work with null entity (for example renderHtml with empty inputs).

It would be nice, if this is support by Doctrine natively, because i have some performace problems with my implementation. If it's interest in my whole code i can send you. But of course there's some security holes so i'll send it privetely. Thanks for understand and for Doctrine of course.

Comment by Mathias Verraes [ 13/Jul/11 ]

Note that Roman's workaround presented here does not work.

   /** @PrePersist @PreUpdate */
   function _destructEmbedded() {
       // destruct $embedded into $value1, $value2, $value3
   }

Doctrine tracks changes and does not perform updates when no changes are found. $embedded is not mapped, so it's not tracked and won't be taken into account by Doctrine when updating. Therefore, if $embedded is the only value that was changed, the PreUpdate event won't be triggered.

The easiest thing to do is to simply destruct the VO on every mutation:

   public function setEmbedded($embedded) {
       $this->embedded = $embedded;
       $this->_destructEmbedded();
   }

The downside is that you need to remember to call the method in every setter, but apart from that, there are no side effects, it always works and it's just one line of code

_constructEmbedded() keeps working as is, postLoad will always be triggered.

Comment by Benjamin Dulau [ 18/Dec/11 ]

Hi,

This feature would be awesome !
VOs are really essential in a good domain design.

If you plan to implement this, please remember that you can have nested VOs.
Take the design for a Booking process for instance, you would have a DateRange object embedding two DateTime objects (in the simplest case).

I have no doubts that you've already took this in consideration, but i prefer pointing this out, just in case

Comment by Benjamin Eberlei [ 20/Jan/12 ]

work has been started, https://github.com/doctrine/doctrine2/pull/265

Comment by Matthias Pigulla [ 23/Nov/12 ]

Does the new "complex sql types" feature help here - I mean, could that be used to map a value object to more than one column in the database?

Comment by songoko songowan [ 10/Feb/13 ]

@Benjamin Eberlei The request seems to be closed in the link you provided! Does that mean that this feature won't be implemented?!

Comment by Marco Pivetta [ 10/Feb/13 ]

songoko songowan no, it just probably wasn't the correct way of implementing this

Comment by Daniel Pitts [ 11/Apr/13 ]

I'm curious if any effort is currently being put into this. I would really love to have this feature available.

Comment by Marco Pivetta [ 11/Apr/13 ]

Daniel Pitts this is being developed in DDC-2374 ( https://github.com/doctrine/doctrine2/pull/634 )

Generated at Sun May 26 03:16:31 UTC 2013 using JIRA 5.2.7#850-sha1:b2af0c8dc8537b36121c6a579fabbdf79fc919e5.