Skip to content


Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge pull request #21 from doctrine/update-associations-doc

update associations documentation
  • Loading branch information...
commit a19e99e5aae86306b4487d8484608b981ba0242d 2 parents ba8b505 + 0e2e885
@dbu dbu authored
Showing with 177 additions and 77 deletions.
  1. +169 −69 en/reference/association-mapping.rst
  2. +8 −8 en/reference/events.rst
238 en/reference/association-mapping.rst
@@ -235,112 +235,212 @@ In ReferenceMany collections, you can even have documents of mixed types.
-Referrers back to the referencing documents
+Referrers to inverse the reference relation
-PHPCR-ODM is able to tell which documents reference a specific document, if the `hard` or
-`weak` strategy is used. The Referrers mapping provides a read only field with a collection
-of all documents referencing this document.
+PHPCR-ODM is able to tell which documents reference a specific document, if the ``hard`` or
+``weak`` strategy is used. The ``Referrers`` mapping is a collection of documents that have
+a reference to this document.
-In ORM terms, the Reference is the owning side of the association, while the Referrer is the inverse side.
+In ORM terms, the Reference is the owning side of the association, while the
+Referrer is the inverse side. Contrary to the ORM, the PHPCR references really
+are directional, they are always stored in the property of the document with
+the ReferenceOne or ReferenceMany field. Referrer is a purely virtual information
+that is not explicitly stored in the PHPCR database but determined at runtime.
-You can use the ``filter`` attribute to limit the collection to a specific property name
-on the referring side. The filter follows the ``name`` parameter of
-`PHPCR\NodeInterface::getReferences() <>`_
+You need to specify the ``referrerDocument`` to specify the (base) class of the
+document that has the reference, and ``referencedBy`` to tell which field of the
+referencing document contains the reference. After flushing, the reference property
+will contain the referenced document.
+.. configuration-block::
+ .. code-block:: php
+ /** @Referrers(referrerDocument="FQN\Class\Name", referencedBy="otherFieldName") */
+ private $specificReferrers;
+ /** @Referrers(referrerDocument="Other\Class\Name", referencedBy="someFieldName", cascade="persist, remove") */
+ private $cascadedReferrers;
+ .. code-block:: xml
+ <doctrine-mapping>
+ <document class="MyPersistentClass">
+ <referrers fieldName="specificReferrers" referrerDocument="FQN\Class\Name" referencedBy="otherFieldName" />
+ <referrers fieldName="cascadedReferrers" referrerDocument="Other\Class\Name" referencedBy="someFieldName" cascade="persist, remove" />
+ </document>
+ </doctrine-mapping>
+ .. code-block:: yaml
+ MyPersistentClass:
+ referrers:
+ specificReferrers:
+ referrerDocument: FQN\Class\Name
+ referencedBy: otherFieldName
+ cascadedReferrers:
+ referrerDocument: Other\Class\Name
+ referencedBy: someFieldName
+ cascade: persist, remove
+Referrers can cascade like the other association mappings to persist or delete their
+referrers if desired.
+.. note::
+ The main use case to persist cascade or deletion of the referrer mapping
+ is to build a form where it is possible to add documents that should reference
+ this content. However, it is not allowed to modify both the reference collection
+ and the referrer collection of interlinked content, as this would be ambiguous.
+.. tip::
+ There is also the ``DocumentManager::getReferrers`` that allows you to control
+ what referencing documents to get more fine grained, if Referrers is to limited
+ and MixedReferrers too broad.
+The mixed referrers is a much simpler but read only mapping to get a collection
+of *all* documents that have a reference to this document. The only possible option
+of mixed referrers is `referenceType` to limit the referrers to only hard resp. weak
+references. If left out, you get both types of references.
+Mixed referrers can even be mapped on a document that is not referenceable, as you
+might do it on a base document of which some extending documents are referenceable.
+An example for this is the `Generic` document provided by phpcr-odm itself.
-Additionally you can filter for only weak or only hard references with ``referenceType`` -
-if you don't you get both types of references.
.. configuration-block::
.. code-block:: php
- /** @Referrers */
+ /** @MixedReferrers */
private $allReferrers;
- /** @Referrers(filter="weakTarget") */
- private $specificReferrers;
- /** @Referrers(referenceType="hard") */
+ /** @MixedReferrers(referenceType="hard") */
private $hardReferrers;
.. code-block:: xml
<document class="MyPersistentClass">
- <referrers fieldName="allReferrers" />
- <referrers fieldName="specificReferrers" filter="weakTarget" />
- <referrers fieldName="hardReferrers" reference-type="hard" />
+ <mixed-referrers fieldName="allReferrers" />
+ <mixed-referrers fieldName="hardReferrers" reference-type="hard" />
.. code-block:: yaml
- referrers:
+ mixedReferrers:
allReferrers: ~
- specificReferrers:
- filter: weakTarget
referenceType: hard
-.. _collections:
+Transitive persistence / Cascade Operations
+Persisting, removing, detaching and merging individual documents can
+become pretty cumbersome, especially when a highly interweaved object graph
+is involved. PHPCR-ODM provides cascading with the same concepts as
+Doctrine2 ORM does.
-TODO: review
-In all the examples of many-valued associations in this manual we
-will make use of a ``Collection`` interface and a corresponding
-default implementation ``ArrayCollection`` that are defined in the
-``Doctrine\Common\Collections`` namespace. Why do we need that?
-Doesn't that couple my domain model to Doctrine? Unfortunately, PHP
-arrays, while being great for many things, do not make up for good
-collections of business objects, especially not in the context of
-an ORM. The reason is that plain PHP arrays can not be
-transparently extended / instrumented in PHP code, which is
-necessary for a lot of advanced ORM features. The classes /
-interfaces that come closest to an OO collection are ArrayAccess
-and ArrayObject but until instances of these types can be used in
-all places where a plain array can be used (something that may
-happen in PHP6) their usability is fairly limited. You "can"
-type-hint on ``ArrayAccess`` instead of ``Collection``, since the
-Collection interface extends ``ArrayAccess``, but this will
-severely limit you in the way you can work with the collection,
-because the ``ArrayAccess`` API is (intentionally) very primitive
-and more importantly because you can not pass this collection to
-all the useful PHP array functions, which makes it very hard to
-work with.
+Each association to another document or a collection of documents can be
+configured to automatically cascade certain operations. For the ``Children`` mapping,
+cascading persist and remove are implicit and cannot be disabled. A PHPCR node
+always must have a parent, removing the parent removes its children.
+The child removal happens on PHPCR level and does not trigger additional
+lifecycle events.
-.. warning::
+For References and Referrers, no operations are cascaded by default, they
+can be configured specifically.
+The following cascade options exist:
+- **persist**: Cascades persist operations to the associated documents.
+- **remove**: Cascades remove operations to the associated documents.
+- **merge**: Cascades merge operations to the associated documents.
+- **detach**: Cascades detach operations to the associated documents.
+- **refresh**: Also refresh the associated documents when refreshing this document.
+- **translation**: Cascade the current translation locale to associated documents.
+- **all**: Cascades persist, remove, merge, detach, refresh and translation
+ operations to associated documents.
+.. note::
- The Collection interface and ArrayCollection class,
- like everything else in the Doctrine namespace, are neither part of
- the ORM, nor the DBAL, it is a plain PHP class that has no outside
- dependencies apart from dependencies on PHP itself (and the SPL).
- Therefore using this class in your domain classes and elsewhere
- does not introduce a coupling to the persistence layer. The
- Collection class, like everything else in the Common namespace, is
- not part of the persistence layer. You could even copy that class
- over to your project if you want to remove Doctrine from your
- project and all your domain classes will work the same as before.
+ Cascade operations are performed in memory. That means collections and related documents
+ are fetched into memory, even if they are still marked as lazy when
+ the cascade operation is about to be performed. This approach allows
+ document lifecycle events to be performed for each of these operations.
+ However, pulling a large object graph into memory on cascade can cause considerable performance
+ overhead, especially when cascading collections are large. Makes sure
+ to weigh the benefits and downsides of each cascade operation that you define.
+Even though automatic cascading is convenient it should be used
+with care. Do not blindly apply ``cascade=all`` to all associations as
+it will unnecessarily degrade the performance of your application.
+For each cascade operation that gets activated Doctrine also
+applies that operation to the association, be it single or
+collection valued.
+Persistence by Reachability: Cascade Persist
+There are additional semantics that apply to the Cascade Persist
+operation. During each flush() operation Doctrine detects if there
+are new documents in any collection and three possible cases can
+1. New documents in a collection marked as cascade persist will be
+ directly persisted by Doctrine.
+2. New documents in a collection not marked as cascade persist will
+ produce an Exception and rollback the flush() operation.
+3. Collections without new documents are skipped.
+This concept is called "Persistence by Reachability". New documents
+that are found on already managed documents are automatically
+persisted as long as the association is defined as cascade
+.. _collections:
+All many-valued associations of PHPCR-ODM use implementations of the ``Collection``
+interface. They are more powerful than plain arrays. Read sections 8.2 to 8.5 in
+the ORM documentation `Working with associations <>`_
+if you are not familiar with associations.
+Your domain models need to use those classes, but they are defined in a
+specific doctrine collections repository and thus not specific to any
+persistence implementation.
+For a discussion of this topic, see the `Collections section <>`_
+in the ORM documentation.
Initializing Collections
-You have to be careful when using entity fields that contain a
-collection of related entities. Say we have a User entity that
+You have to be careful when using document fields that contain a
+collection of related documents. Say we have a User document that
contains a collection of groups:
.. code-block:: php
- /** @Entity **/
+ /** @Document **/
class User
- /** @ManyToMany(targetEntity="Group") **/
+ /** @ReferenceMany **/
private $groups;
public function getGroups()
@@ -352,21 +452,21 @@ contains a collection of groups:
With this code alone the ``$groups`` field only contains an
instance of ``Doctrine\Common\Collections\Collection`` if the user
is retrieved from Doctrine, however not after you instantiated a
-fresh instance of the User. When your user entity is still new
+fresh instance of the User. When your user document is still new
``$groups`` will obviously be null.
This is why we recommend to initialize all collection fields to an
-empty ``ArrayCollection`` in your entities constructor:
+empty ``ArrayCollection`` in your documents constructor:
.. code-block:: php
use Doctrine\Common\Collections\ArrayCollection;
- /** @Entity **/
+ /** @Document **/
class User
- /** @ManyToMany(targetEntity="Group") **/
+ /** @ReferenceMany **/
private $groups;
public function __construct()
@@ -380,12 +480,12 @@ empty ``ArrayCollection`` in your entities constructor:
-Now the following code will be working even if the Entity hasn't
-been associated with an EntityManager yet:
+Now the following code will be working even if the Document hasn't
+been associated with a DocumentManager yet:
.. code-block:: php
- $group = $entityManager->find('Group', $groupId);
+ $group = $documentManager->find(null, $groupId);
$user = new User();
16 en/reference/events.rst
@@ -61,13 +61,13 @@ Event order when moving
During the flush() operation of a modified document, the events get triggered in the following order:
-1. preFlush
-2. onFlush
-3. preUpdate
-4. postUpdate
-5. preMove
-6. postMove
-7. postFlush
+* 1. preFlush
+* 2. onFlush
+* 3. preUpdate
+* 4. postUpdate
+* 5. preMove
+* 6. postMove
+* 7. postFlush
As the move event is triggered after the changeset has been calculated,
@@ -165,4 +165,4 @@ listed in the previous Lifecycle Events section.
Listening to Lifecycle Events
-This works exactly the same as with the `ORM events <>`_.
+This works exactly the same as with the `ORM events <>`_.
Please sign in to comment.
Something went wrong with that request. Please try again.