Permalink
Browse files

Merge pull request #268 from doctrine/collection_lazy_loading

improved lazy loading for ChildrenCollection's
  • Loading branch information...
2 parents b7f40b9 + 659df23 commit 84ad950df08b8d6c23f98d12004384764335bb1e @lsmith77 lsmith77 committed Mar 19, 2013
@@ -183,7 +183,7 @@
/** @PHPCR\ReferenceOne */
public $reference;
- /** @PHPCR\Referrers */
+ /** @PHPCR\MixedReferrers */
public $referrers;
...
@@ -238,7 +238,7 @@
$query->setLimit($limit);
$query->setOffset($offset);
- $collection = $dm->getDocumentsByQuery($query);
+ $collection = $dm->getDocumentsByPhpcrQuery($query);
</pre>
</div>
@@ -248,14 +248,16 @@
$qb = $dm->createQueryBuilder();
$factory = $qb->getQOMFactory();
- // SELECT * FROM nt:unstructured WHERE name NOT IS NULL
- $qb->from($factory->selector('nt:unstructured'))
- ->where($factory->propertyExistence('name'))
- ->setFirstResult(10)
- ->setMaxResults(10)
+ // SELECT * FROM [nt:unstructured] WHERE text = 'foo' ORDER BY title
+ $qb = $conn->createQueryBuilder()
+ ->nodeType('nt:unstructured')
+ ->where($qb->expr()->eq('text', 'foo'))
+ ->orderBy('title')
+ ->setMaxResults($limit)
+ ->setFirstResult($offset)
->execute();
- $collection = $dm->getDocumentsByQuery($qb->getQuery());
+ $collection = $dm->getDocumentsByPhpcrQuery($qb->getQuery());
</pre>
</div>
@@ -356,7 +358,6 @@
$locales = $dm->getLocalesFor($article);
var_dump($locales); // en, fr
-
</pre>
</div>
@@ -20,6 +20,7 @@
namespace Doctrine\ODM\PHPCR;
use Doctrine\Common\Collections\ArrayCollection;
+use PHPCR\Util\PathHelper;
/**
* Children collection class
@@ -33,15 +34,16 @@ class ChildrenCollection extends PersistentCollection
private $document;
private $filter;
private $fetchDepth;
- private $originalNodeNames = array();
+ private $originalNodeNames;
+ private $node;
/**
* Creates a new persistent collection.
*
* @param DocumentManager $dm The DocumentManager the collection will be associated with.
* @param object $document Document instance
* @param string $filter filter string
- * @param integer $fetchDepth optional fetch depth if supported by the PHPCR session
+ * @param null|int $fetchDepth optional fetch depth
* @param string $locale the locale to use during the loading of this collection
*/
public function __construct(DocumentManager $dm, $document, $filter = null, $fetchDepth = null, $locale = null)
@@ -53,40 +55,147 @@ public function __construct(DocumentManager $dm, $document, $filter = null, $fet
$this->locale = $locale;
}
+ private function getNode()
+ {
+ if (null === $this->node) {
+ $path = $this->dm->getUnitOfWork()->getDocumentId($this->document);
+ $this->node = $this->dm->getPhpcrSession()->getNode($path, $this->fetchDepth);
+ }
+
+ return $this->node;
+ }
+
+ private function getChildren($childNodes)
+ {
+ $uow = $this->dm->getUnitOfWork();
+ $locale = $this->locale ?: $uow->getCurrentLocale($this->document);
+
+ $childDocuments = array();
+ foreach ($childNodes as $childNode) {
+ $childDocuments[$childNode->getName()] = $uow->getOrCreateProxyFromNode($childNode, $locale);
+ }
+
+ return $childDocuments;
+ }
+
/**
* Initializes the collection by loading its contents from the database
* if the collection is not yet initialized.
*/
public function initialize()
{
if (!$this->initialized) {
+ $this->getOriginalNodeNames();
+ $childNodes = $this->getNode()->getNodes($this->filter, $this->fetchDepth);
+ $this->collection = new ArrayCollection($this->getChildren($childNodes));
$this->initialized = true;
- $this->collection = $this->dm->getChildren($this->document, $this->filter, $this->fetchDepth, $this->locale);
- $this->originalNodeNames = $this->collection->getKeys();
}
}
- /**
- * Return the ordered list of node names of children that existed when the collection was initialized
- *
- * @return array
- */
- public function getOriginalNodeNames()
+ /** {@inheritDoc} */
+ public function contains($element)
{
- $this->initialize();
+ if (!$this->initialized) {
+ $uow = $this->dm->getUnitOfWork();
- return $this->originalNodeNames;
+ // Shortcut for new documents
+ $documentState = $uow->getDocumentState($element);
+
+ if ($documentState === UnitOfWork::STATE_NEW) {
+ return false;
+ }
+
+ // Document is scheduled for inclusion
+ if ($documentState === UnitOfWork::STATE_MANAGED && $uow->isScheduledForInsert($element)) {
+ return false;
+ }
+
+ $documentId = $uow->getDocumentId($element);
+ if (PathHelper::getParentPath($documentId) !== PathHelper::getParentPath($uow->getDocumentId($this->document))) {
+ return false;
+ }
+
+ $nodeName = PathHelper::getNodeName($documentId);
+ return in_array($nodeName, $this->getOriginalNodeNames());
+ }
+
+ return parent::contains($element);
+ }
+
+ /** {@inheritDoc} */
+ public function containsKey($key)
+ {
+ if (!$this->initialized) {
+ return in_array($key, $this->getOriginalNodeNames());
+ }
+
+ return parent::containsKey($key);
+ }
+
+ /** {@inheritDoc} */
+ public function count()
+ {
+ if (!$this->initialized) {
+ return count($this->getOriginalNodeNames());
+ }
+
+ return parent::count();
+ }
+
+ /** {@inheritDoc} */
+ public function isEmpty()
+ {
+ if (!$this->initialized) {
+ return !$this->count();
+ }
+
+ return parent::isEmpty();
+ }
+
+ /** {@inheritDoc} */
+ public function slice($offset, $length = null)
+ {
+ if (!$this->initialized) {
+ $nodeNames = $this->getOriginalNodeNames();
+ if (!is_numeric($offset)) {
+ $offset = array_search($offset, $nodeNames);
+ if (false === $offset) {
+ return new ArrayCollection();
+ }
+ }
+
+ $nodeNames = array_slice($nodeNames, $offset, $length);
+ $parentPath = $this->getNode()->getPath();
+ array_walk($nodeNames, function (&$nodeName) use ($parentPath) {
+ $nodeName = "$parentPath/$nodeName";
+ });
+
+ $childNodes = $this->dm->getPhpcrSession()->getNodes($nodeNames);
+ return $this->getChildren($childNodes);
+ }
+
+ if (!is_numeric($offset)) {
+ $nodeNames = $this->collection->getKeys();
+ $offset = array_search($offset, $nodeNames);
+ if (false === $offset) {
+ return new ArrayCollection();
+ }
+ }
+
+ return parent::slice($offset, $length);
}
/**
- * @return ArrayCollection The collection
+ * Return the ordered list of node names of children that existed when the collection was initialized
+ *
+ * @return array
*/
- public function unwrap()
+ public function getOriginalNodeNames()
{
- if (!$this->initialized) {
- return new ArrayCollection();
+ if (null === $this->originalNodeNames) {
+ $this->originalNodeNames = (array) $this->getNode()->getNodeNames($this->filter);
}
- return parent::unwrap();
+ return $this->originalNodeNames;
}
}
@@ -515,7 +515,7 @@ public function createQuery($statement, $language)
/**
* Create the fluent query builder.
*
- * After building your query, use DocumentManager::getDocumentsByQuery with the
+ * After building your query, use DocumentManager::getDocumentsByPhpcrQuery with the
* query returned by QueryBuilder::getQuery()
*
* @return QueryBuilder
@@ -804,10 +804,10 @@ public function refresh($document)
*
* @param object $document document instance which children should be loaded
* @param string|array $filter optional filter to filter on children names
- * @param integer $fetchDepth optional fetch depth if supported by the PHPCR session
+ * @param integer $fetchDepth optional fetch depth
* @param string $locale the locale to use during the loading of this collection
*
- * @return \Doctrine\Common\Collections\Collection collection of child documents
+ * @return ChildrenCollection collection of child documents
*/
public function getChildren($document, $filter = null, $fetchDepth = null, $locale = null)
{
@@ -817,7 +817,7 @@ public function getChildren($document, $filter = null, $fetchDepth = null, $loca
$this->errorIfClosed();
- return $this->unitOfWork->getChildren($document, $filter, $fetchDepth, $locale);
+ return new ChildrenCollection($this, $document, $filter, $fetchDepth, $locale);
}
/**
@@ -835,7 +835,7 @@ public function getChildren($document, $filter = null, $fetchDepth = null, $loca
* @param string|null $name optional PHPCR property name that holds the reference
* @param string $locale the locale to use during the loading of this collection
*
- * @return \Doctrine\Common\Collections\Collection collection of referrer documents
+ * @return ReferrersCollection collection of referrer documents
*/
public function getReferrers($document, $type = null, $name = null, $locale = null)
{
@@ -845,7 +845,7 @@ public function getReferrers($document, $type = null, $name = null, $locale = nu
$this->errorIfClosed();
- return $this->unitOfWork->getReferrers($document, $type, $name, $locale);
+ return new ReferrersCollection($this, $document, $type, $name, $locale);
}
/**
@@ -55,8 +55,6 @@ public function __construct(DocumentManager $dm, array $referencedNodes, $target
public function initialize()
{
if (!$this->initialized) {
- $this->initialized = true;
-
$referencedDocs = array();
$referencedNodes = $this->dm->getPhpcrSession()->getNodesByIdentifier($this->referencedNodes);
$uow = $this->dm->getUnitOfWork();
@@ -73,9 +71,11 @@ public function initialize()
}
$this->collection = new ArrayCollection($referencedDocs);
+ $this->initialized = true;
}
}
+ /** {@inheritDoc} */
public function count()
{
if (!$this->initialized) {
@@ -85,8 +85,13 @@ public function count()
return parent::count();
}
+ /** {@inheritDoc} */
public function isEmpty()
{
- return !$this->count();
+ if (!$this->initialized) {
+ return !$this->count();
+ }
+
+ return parent::isEmpty();
}
}
@@ -50,21 +50,39 @@ public function __construct(DocumentManager $dm, $document, $type = null, $name
public function initialize()
{
if (!$this->initialized) {
- $this->initialized = true;
+ $uow = $this->dm->getUnitOfWork();
+ $node = $this->dm->getPhpcrSession()->getNode($uow->getDocumentId($this->document));
- $this->collection = $this->dm->getReferrers($this->document, $this->type, $this->name, $this->locale);
- }
- }
+ $referrerDocuments = array();
+ $referrerPropertiesW = array();
+ $referrerPropertiesH = array();
- /**
- * @return ArrayCollection The collection
- */
- public function unwrap()
- {
- if (!$this->initialized) {
- return new ArrayCollection();
- }
+ switch ($this->type) {
+ case 'weak':
+ $referrerPropertiesW = $node->getWeakReferences($this->name);
+ break;
+ case 'hard':
+ $referrerPropertiesH = $node->getReferences($this->name);
+ break;
+ default:
+ $referrerPropertiesW = $node->getWeakReferences($this->name);
+ $referrerPropertiesH = $node->getReferences($this->name);
+ }
+
+ $locale = $this->locale ?: $uow->getCurrentLocale($this->document);
+
+ foreach ($referrerPropertiesW as $referrerProperty) {
+ $referrerNode = $referrerProperty->getParent();
+ $referrerDocuments[] = $uow->getOrCreateProxyFromNode($referrerNode, $locale);
+ }
- return parent::unwrap();
+ foreach ($referrerPropertiesH as $referrerProperty) {
+ $referrerNode = $referrerProperty->getParent();
+ $referrerDocuments[] = $uow->getOrCreateProxyFromNode($referrerNode, $locale);
+ }
+
+ $this->collection = new ArrayCollection($referrerDocuments);
+ $this->initialized = true;
+ }
}
}
Oops, something went wrong.

0 comments on commit 84ad950

Please sign in to comment.