The doctrine ORM and the mongodb ODM use Proxy classes for referenced documents. Those classes store the ID of the document and only fetch the document from the data store once a public method on the object is invoked.
PHPCR-ODM uses the same mechanism. The proxies simply store the uuid or path. This avoids loading the whole repository tree if reference to other nodes are used: @Child, @Children, @Parent, @ReferenceOne, @ReferenceMany and @Referers.
Whereever we have more than one node, we could implement some iterator that allows for lazy loading from the underlying PHPCR data. For the one node cases, we could build optimized proxies later that only load the PHPCR node on demand. (ReferenceOne, Parent, Child).
TODO: how is this now implemented? To create the proxy classes Doctrine ORM and Mongodb ODM use a Symfony console command. This adds a dependency on the Console component. Having the console would allow for other commands as well like loading fixtures, checking the setup etc.
A @Parent annotation can be used to access the parent of the annotated node. A use case for this could be a menu item that needs a list of its siblings ... As discussed in other places, a move is its own operation. Therefore @Parent is immutable. However it can be used to place new nodes in the tree together with the @NodeName annotation.
As discussed on the list there will be an @Children annotation that can be filtered on names of the children like PHPCR Node::getNodes does. A Document can have several @Children annotation to access different subsets of its children. For a start these collections should be immutable and should contain only children not descendants of the node. Since PHPCR Node::getNodes returns an Iterator we might write an IteratorCollection wrapper that implements Doctrine Collection (see http://www.doctrine-project.org/api/common/2.0/doctrine/common/collections/collection.html).
The @Child annotation can be used to assign and read a single child. Currently the name of the child is defined by the annotation but this should be more flexible. E.g. the parent can have a property that contains the name of the child. => in PHPCR this would be getting the node from a PATH property...
PHPCR already implements references through the property types REFERENCE and WEAKREFERENCE. @ReferenceOne is a single valued property of either type REFERENCE or WEAKREFERENCE. @ReferenceMany is a similar multivalued property. The reference annotations have an attribute
$weak = true
which denotes the type of the reference, weak or not. Weak is the default. ReferenceMany is implemented as a Doctrine ArrayCollection. Since we can directly get the nodes from the properties, again proxies can be used. To optimize ReferenceMany, we should wrap the array returned by property::getString (which is a list of node ids) into an iterator to only load referenced nodes from the backend one by one.
Every referenced node in PHPCR can produce a list of the nodes that reference the node through the getReferences and getWeakReferences methods. This is implemented as a immutable (to keep things simple for a start) collection. A use case could be a list of all usages of an asset e.g. an image in an asset library or a list of all menu items that reference a content node (e.g. for highlighting the current menu item).
Properties with the annotation @Referrers store an immutable collection of all documents that have a property with a reference to the actual document. You can specify the optional parameter filterName. Its value is passed to the name parameter of Node::getReferences() or Node::getWeakReferences(). You can also specify an optional parameter referenceType, with the values weak or hard (and null), to only get documents that have either a weak or a hard reference to this document. If you specify null then all documents with weak or hard references are fetched, which is also the default behavior.
To be on the save side referencable nodes should declare this in the @Document so that we can add the proper mixin at node creation. For this a new attribute
$referencable = false