Skip to content
Browse files

referrer annotation

  • Loading branch information...
1 parent 8785fa3 commit 42c24a3d919e8c83743ddeb0031c6fea71b61fcb Johannes Stark committed Aug 16, 2011
View
14 lib/Doctrine/ODM/PHPCR/DocumentManager.php
@@ -318,6 +318,20 @@ public function getChildren($document, $filter = null)
}
/**
+ * Get the documents that refer a given document using an optional name.
+ *
+ * This methods gets all nodes as a collection of documents that refer the
+ * given document and matches a given name.
+ * @param $document document instance which referrers should be loaded
+ * @param string|array $name optional name to match on referrers names
+ * @return a collection of referrer documents
+ */
+ public function getReferrers($document, $name = null)
+ {
+ return $this->unitOfWork->getReferrers($document, $name);
+ }
+
+ /**
* Flush all current changes, that is save them within the phpcr session
* and commit that session to permanent storage.
*/
View
4 lib/Doctrine/ODM/PHPCR/Mapping/Annotations/DoctrineAnnotations.php
@@ -92,6 +92,10 @@ class Children extends Annotation
{
public $filter = null;
}
+class Referrers extends Annotation
+{
+ public $filterName = null;
+}
final class EmbeddedDocument extends Annotation {}
final class EmbedOne extends Property {}
View
13 lib/Doctrine/ODM/PHPCR/Mapping/ClassMetadataInfo.php
@@ -192,6 +192,11 @@ class ClassMetadataInfo implements ClassMetadata
public $childrenMappings = array();
/**
+ * Mapping of referrers: access referrer nodes through a collection
+ */
+ public $referrersMappings = array();
+
+ /**
* PHPCR documents are always versioned, this flag determines if this version is exposed to the userland.
*
* @var bool
@@ -520,6 +525,13 @@ public function mapChildren(array $mapping)
$this->childrenMappings[$mapping['fieldName']] = $mapping;
}
+ public function mapReferrers(array $mapping)
+ {
+ $mapping = $this->validateAndCompleteFieldMapping($mapping, false);
+ $mapping['name'] = $mapping['fieldName'];
+ $this->referrersMappings[$mapping['fieldName']] = $mapping;
+ }
+
protected function validateAndCompleteFieldMapping($mapping, $isField = true)
{
if (!isset($mapping['fieldName'])) {
@@ -532,6 +544,7 @@ protected function validateAndCompleteFieldMapping($mapping, $isField = true)
|| isset($this->associationsMappings[$mapping['fieldName']])
|| isset($this->childMappings[$mapping['fieldName']])
|| isset($this->childrenMappings[$mapping['fieldName']])
+ || isset($this->referrersMappings[$mapping['fieldName']])
) {
throw MappingException::duplicateFieldMapping($this->name, $mapping['fieldName']);
}
View
3 lib/Doctrine/ODM/PHPCR/Mapping/Driver/AnnotationDriver.php
@@ -174,6 +174,9 @@ public function loadMetadataForClass($className, ClassMetadata $class)
} elseif ($fieldAnnot instanceof \Doctrine\ODM\PHPCR\Mapping\Annotations\ReferenceMany) {
$mapping = array_merge($mapping, (array) $fieldAnnot);
$class->mapManyToMany($mapping);
+ } elseif ($fieldAnnot instanceof \Doctrine\ODM\PHPCR\Mapping\Annotations\Referrers) {
+ $mapping = array_merge($mapping, (array) $fieldAnnot);
+ $class->mapReferrers($mapping);
}
}
}
View
31 lib/Doctrine/ODM/PHPCR/ReferrersCollection.php
@@ -0,0 +1,31 @@
+<?php
+
+namespace Doctrine\ODM\PHPCR;
+
+/**
+ * Referrer collection class
+ *
+ * This class represents a collection of referrers of a document which phpcr
+ * names match a optional name
+ *
+ */
+class ReferrersCollection extends PersistentCollection
+{
+ private $document;
+ private $dm;
+ private $name;
+
+ public function __construct($document, DocumentManager $dm, $name= null)
+ {
+ $this->document = $document;
+ $this->dm = $dm;
+ $this->name = $name;
+ }
+
+ protected function load()
+ {
+ if (null === $this->col) {
+ $this->col = $this->dm->getReferrers($this->document, $this->name);
+ }
+ }
+}
View
68 lib/Doctrine/ODM/PHPCR/UnitOfWork.php
@@ -339,6 +339,10 @@ public function createDocument($documentName, $node, array &$hints = array())
$documentState[$mapping['fieldName']] = new ChildrenCollection($document, $this->dm, $mapping['filter']);
}
+ foreach ($class->referrersMappings as $mapping) {
+ $documentState[$mapping['fieldName']] = new ReferrersCollection($document, $this->dm, $mapping['filterName']);
+ }
+
if (isset($documentName) && $this->validateDocumentName && !($document instanceof $documentName)) {
$msg = "Doctrine metadata mismatch! Requested type '$documentName' type does not match type '{$class->name}' stored in the metadata";
throw new \InvalidArgumentException($msg);
@@ -449,6 +453,7 @@ private function cascadeScheduleInsert($class, $document, &$visited)
}
}
}
+
foreach ($class->childMappings as $childName => $mapping) {
$child = $class->reflFields[$childName]->getValue($document);
if ($child !== null && $this->getDocumentState($child) == self::STATE_NEW) {
@@ -458,6 +463,18 @@ private function cascadeScheduleInsert($class, $document, &$visited)
$this->doScheduleInsert($child, $visited, ClassMetadata::GENERATOR_TYPE_ASSIGNED);
}
}
+
+ foreach ($class->referrersMappings as $referrerName => $mapping) {
+ $referrer = $class->reflFields[$referrerName]->getValue($document);
+ if ($referrer !== null && $this->getDocumentState($referrer) == self::STATE_NEW) {
+ //$referrerClass = $this->dm->getClassMetadata(get_class($referrer));
+ //$id = $class->reflFields[$class->identifier]->getValue($document);
+ //$referrerClass->reflFields[$referrerClass->identifier]->setValue($referrer, $id . '/'. $mapping['name']);
+ //$this->doScheduleInsert($referrer, $visited, ClassMetadata::GENERATOR_TYPE_ASSIGNED);
+ $this->doScheduleInsert($referrer, $visited);
+ }
+ }
+
}
private function getIdGenerator($type)
@@ -578,6 +595,7 @@ public function computeChangeSet(ClassMetadata $class, $document)
if (!isset($class->fieldMappings[$fieldName])
&& !isset($class->childMappings[$fieldName])
&& !isset($class->associationsMappings[$fieldName])
+ && !isset($class->referrersMappings[$fieldName])
) {
continue;
}
@@ -623,6 +641,14 @@ public function computeChangeSet(ClassMetadata $class, $document)
}
}
}
+
+ foreach ($class->referrersMappings as $name => $referrerMapping) {
+ if ($this->originalData[$oid][$name]) {
+ foreach ($this->originalData[$oid][$name] as $referrer) {
+ $this->computeReferrerChanges($referrerMapping, $referrer, $id);
+ }
+ }
+ }
}
/**
@@ -666,6 +692,26 @@ private function computeReferenceChanges($mapping, $reference, $referrerId)
}
}
+ /**
+ * Computes the changes of a referrer.
+ *
+ * @param mixed $referrer the referenced document.
+ */
+ private function computeReferrerChanges($mapping, $referrer, $referenceId)
+ {
+ $targetClass = $this->dm->getClassMetadata(get_class($referrer));
+ $state = $this->getDocumentState($referrer);
+ $oid = spl_object_hash($referrer);
+ if ($state == self::STATE_NEW) {
+ $this->persistNew($targetClass, $referrer, ClassMetadata::GENERATOR_TYPE_ASSIGNED);
+ $this->computeChangeSet($targetClass, $referrer);
+ } else if ($state == self::STATE_DETACHED) {
+ // TODO: can this actually happen?
+ throw new \InvalidArgumentException("A detached document was found through a "
+ . "reference during cascading a persist operation.");
+ }
+ }
+
/**
* Gets the changeset for an document.
@@ -1105,6 +1151,28 @@ public function getChildren($document, $filter = null)
}
/**
+ * Get the documents that refer a given document using an optional name.
+ *
+ * This methods gets all nodes as a collection of documents that refer the
+ * given document and matches a given name.
+ * @param $document document instance which referrers should be loaded
+ * @param string|array $name optional name to match on referrers names
+ * @return a collection of referrer documents
+ */
+ public function getReferrers($document, $name = null)
+ {
+ $oid = spl_object_hash($document);
+ $node = $this->nodesMap[$oid];
+ $referrerProperties = $node->getReferences($name);
+ $referrerDocuments = array();
+ foreach ($referrerProperties as $name => $referrerProperty) {
+ $referrerNode = $referrerProperty->getParent();
+ $referrerDocuments[$name] = $this->createDocument(null, $referrerNode);
+ }
+ return new ArrayCollection($referrerDocuments);
+ }
+
+ /**
* Get the PHPCR revision of the document that was current upon retrieval.
*
* @throws PHPCRException
View
393 tests/Doctrine/Tests/ODM/PHPCR/Functional/ReferrerTest.php
@@ -0,0 +1,393 @@
+<?php
+
+namespace Doctrine\Tests\ODM\PHPCR\Functional;
+use Doctrine\ODM\PHPCR\Mapping\Annotations as PHPCRODM;
+
+/**
+ * @group functional
+ */
+class ReferrerTest extends \Doctrine\Tests\ODM\PHPCR\PHPCRFunctionalTestCase
+{
+ public function setUp()
+ {
+ $this->dm = $this->createDocumentManager();
+
+ $this->session = $this->dm->getPhpcrSession();
+ $root = $this->session->getNode('/');
+
+ if ($root->hasNode('functional')) {
+ $root->getNode('functional')->remove();
+ $this->session->save();
+ }
+
+ $this->node = $root->addNode('functional');
+
+ $this->session->save();
+ }
+
+ public function testCreate()
+ {
+ $referrerTestObj = new ReferrerTestObj();
+ $referrerRefTestObj = new ReferrerRefTestObj();
+
+ $referrerTestObj->id = "/functional/referrerTestObj";
+ $referrerTestObj->name = "referrer";
+ $referrerRefTestObj->id = "/functional/referrerRefTestObj";
+ $referrerRefTestObj->name = "referenced";
+
+ $referrerTestObj->reference = $referrerRefTestObj;
+
+ $this->dm->persist($referrerTestObj);
+ $this->dm->flush();
+ $this->dm->clear();
+
+ $reference = $this->dm->find(null, "/functional/referrerRefTestObj");
+
+ $this->assertEquals(count($reference->referrers), 1);
+ $this->assertEquals($reference->referrers->first()->id, "/functional/referrerTestObj");
+ }
+
+ public function testCreateWithoutRef()
+ {
+ $referrerTestObj = new ReferrerRefTestObj();
+ $referrerTestObj->name = 'referrerRefTestObj';
+ $referrerTestObj->id = '/functional/referrerRefTestObj';
+
+ $this->dm->persist($referrerTestObj);
+ $this->dm->flush();
+ $this->dm->clear();
+
+ $document = $this->dm->find(null, '/functional/referrerRefTestObj');
+
+ $this->assertEquals(count($document->referrers), 0);
+ }
+
+ public function testCreateManyRef()
+ {
+ $max = 5;
+
+ $referrerRefTestObj = new ReferrerRefTestObj();
+ $referrerRefTestObj->id = "/functional/referrerRefTestObj";
+ $referrerRefTestObj->name = "referenced";
+
+ $ids = array();
+ for ($i = 0; $i < $max; $i++) {
+ $referrerTestObj = new ReferrerTestObj();
+ $referrerTestObj->id = "/functional/referrerTestObj$i";
+ $referrerTestObj->name = "referrer $i";
+ $referrerTestObj->reference = $referrerRefTestObj;
+ $this->dm->persist($referrerTestObj);
+ $ids[] = "/functional/referrerTestObj$i";
+ }
+
+ $this->dm->flush();
+ $this->dm->clear();
+
+ $reference = $this->dm->find(null, "/functional/referrerRefTestObj");
+
+ $this->assertEquals(count($reference->referrers), $max);
+
+ $tmpIds = array();
+ foreach ($reference->referrers as $referrer) {
+ $tmpIds[] = $referrer->id;
+ }
+
+ foreach ($ids as $id) {
+ $this->assertTrue(in_array($id, $tmpIds));
+ }
+ }
+
+ public function testCreateAddRefLater()
+ {
+ $referrerTestObj = new ReferrerTestObj();
+ $referrerTestObj->name = "referrer";
+ $referrerTestObj->id = "/functional/referrerTestObj";
+
+ $this->dm->persist($referrerTestObj);
+
+ $referrerRefTestObj = new ReferrerRefTestObj();
+ $referrerRefTestObj->id = "/functional/referrerRefTestObj";
+ $referrerRefTestObj->name = "referenced";
+
+ $this->dm->persist($referrerRefTestObj);
+
+ $this->dm->flush();
+ $this->dm->clear();
+
+ $reference = $this->dm->find(null, "/functional/referrerRefTestObj");
+ $this->assertEquals(count($reference->referrers), 0);
+
+ $referrer = $this->dm->find(null, "/functional/referrerTestObj");
+
+ $referrer->reference = $reference;
+
+ $this->dm->flush();
+ $this->dm->clear();
+
+ $tmpReference = $this->dm->find(null, "/functional/referrerRefTestObj");
+
+ $this->assertEquals(count($tmpReference->referrers), 1);
+ $this->assertEquals($tmpReference->referrers->first()->id, "/functional/referrerTestObj");
+ }
+
+ public function testUpdate()
+ {
+ $referrerTestObj = new ReferrerTestObj();
+ $referrerRefTestObj = new ReferrerRefTestObj();
+
+ $referrerTestObj->id = "/functional/referrerTestObj";
+ $referrerTestObj->name = "referrer";
+ $referrerRefTestObj->id = "/functional/referrerRefTestObj";
+ $referrerRefTestObj->name = "referenced";
+
+ $referrerTestObj->reference = $referrerRefTestObj;
+
+ $this->dm->persist($referrerTestObj);
+ $this->dm->flush();
+ $this->dm->clear();
+
+ $reference = $this->dm->find(null, "/functional/referrerRefTestObj");
+ $reference->referrers->first()->name = "referrer changed";
+
+ $this->dm->flush();
+ $this->dm->clear();
+
+ $referrer = $this->dm->find(null, "/functional/referrerTestObj");
+ $this->assertEquals($referrer->name, "referrer changed");
+ }
+
+ public function testUpdateMany()
+ {
+ $max = 5;
+
+ $referrerRefTestObj = new ReferrerRefTestObj();
+ $referrerRefTestObj->id = "/functional/referrerRefTestObj";
+ $referrerRefTestObj->name = "referenced";
+
+ $ids = array();
+ for ($i = 0; $i < $max; $i++) {
+ $referrerTestObj = new ReferrerTestObj();
+ $referrerTestObj->id = "/functional/referrerTestObj$i";
+ $referrerTestObj->name = "referrer $i";
+ $referrerTestObj->reference = $referrerRefTestObj;
+ $this->dm->persist($referrerTestObj);
+ $ids[] = "/functional/referrerTestObj$i";
+ }
+
+ $this->dm->flush();
+ $this->dm->clear();
+
+ $reference = $this->dm->find(null, "/functional/referrerRefTestObj");
+
+ $i = 0;
+ $names = array();
+ foreach ($reference->referrers as $referrer) {
+ $newName = "new name ".$i;
+ $names[] = $newName;
+ $referrer->name = $newName;
+ $i++;
+ }
+
+ $this->dm->flush();
+ $this->dm->clear();
+
+ $tmpNames = array();
+ for ($i = 0; $i < $max; $i++) {
+ $tmpNames[] = $this->dm->find(null, "/functional/referrerTestObj$i")->name;
+ }
+
+ foreach ($names as $name) {
+ $this->assertTrue(in_array($name, $tmpNames));
+ }
+ }
+
+ public function testUpdateOneInMany()
+ {
+ $max = 5;
+
+ $referrerRefTestObj = new ReferrerRefTestObj();
+ $referrerRefTestObj->id = "/functional/referrerRefTestObj";
+ $referrerRefTestObj->name = "referenced";
+
+ $ids = array();
+ for ($i = 0; $i < $max; $i++) {
+ $referrerTestObj = new ReferrerTestObj();
+ $referrerTestObj->id = "/functional/referrerTestObj$i";
+ $referrerTestObj->name = "referrer $i";
+ $referrerTestObj->reference = $referrerRefTestObj;
+ $this->dm->persist($referrerTestObj);
+ $ids[$i] = "/functional/referrerTestObj$i";
+ }
+
+ $this->dm->flush();
+ $this->dm->clear();
+
+ $reference = $this->dm->find(null, "/functional/referrerRefTestObj");
+
+ $i = 0;
+ $names = array();
+ foreach ($reference->referrers as $referrer) {
+ if ($i !== 2) {
+ $names[] = $referrer->name;
+ } else {
+ $newName = "new name ".$i;
+ $referrer->name = $newName;
+ $names[] = $newName;
+ }
+ $i++;
+ }
+
+ $this->dm->flush();
+ $this->dm->clear();
+
+ $tmpNames = array();
+ for ($i = 0; $i < $max; $i++) {
+ $tmpNames[] = $this->dm->find(null, "/functional/referrerTestObj$i")->name;
+ }
+
+ foreach ($names as $name) {
+ $this->assertTrue(in_array($name, $tmpNames));
+ }
+ }
+
+ public function testRemoveReferrer()
+ {
+ $referrerTestObj = new ReferrerTestObj();
+ $referrerRefTestObj = new ReferrerRefTestObj();
+
+ $referrerTestObj->id = "/functional/referrerTestObj";
+ $referrerTestObj->name = "referrer";
+ $referrerRefTestObj->id = "/functional/referrerRefTestObj";
+ $referrerRefTestObj->name = "referenced";
+
+ $referrerTestObj->reference = $referrerRefTestObj;
+
+ $this->dm->persist($referrerTestObj);
+ $this->dm->flush();
+ $this->dm->clear();
+
+ $reference = $this->dm->find(null, "/functional/referrerRefTestObj");
+
+ $this->assertEquals(count($reference->referrers), 1);
+ $this->assertEquals($reference->referrers->first()->id, "/functional/referrerTestObj");
+
+ $referrer = $this->dm->find(null, "/functional/referrerTestObj");
+ $this->dm->remove($referrer);
+
+ $this->dm->flush();
+ $this->dm->clear();
+
+ $this->assertNull($this->dm->find(null, "/functional/referrerTestObj"));
+
+ $reference = $this->dm->find(null, "/functional/referrerRefTestObj");
+
+ $this->assertEquals(count($reference->referrers), 0);
+ $this->assertFalse($reference->referrers->first());
+ }
+
+ public function testRemoveReferrerMany()
+ {
+ $max = 5;
+
+ $referrerRefTestObj = new ReferrerRefTestObj();
+ $referrerRefTestObj->id = "/functional/referrerRefTestObj";
+ $referrerRefTestObj->name = "referenced";
+
+ $ids = array();
+ for ($i = 0; $i < $max; $i++) {
+ $referrerTestObj = new ReferrerTestObj();
+ $referrerTestObj->id = "/functional/referrerTestObj$i";
+ $referrerTestObj->name = "referrer $i";
+ $referrerTestObj->reference = $referrerRefTestObj;
+ $this->dm->persist($referrerTestObj);
+ $ids[$i] = "/functional/referrerTestObj$i";
+ }
+
+ $this->dm->flush();
+ $this->dm->clear();
+
+ $delete = 2;
+ $delRef = $this->dm->find(null, "/functional/referrerTestObj$delete");
+ $this->dm->remove($delRef);
+ unset($ids[$delete]);
+
+ $this->dm->flush();
+ $this->dm->clear();
+
+ $reference = $this->dm->find(null, "/functional/referrerRefTestObj");
+
+ $this->assertEquals(count($reference->referrers), $max -1);
+
+ $tmpIds = array();
+ foreach ($reference->referrers as $referrer) {
+ $tmpIds[] = $referrer->id;
+ }
+
+ foreach ($ids as $id) {
+ $this->assertTrue(in_array($id, $tmpIds));
+ }
+ }
+
+ /**
+ * Remove referenced node, but change referrer node before
+ */
+
+ public function testRemoveReferrerChangeBevore()
+ {
+
+ $referrerTestObj = new ReferrerTestObj();
+ $referrerRefTestObj = new ReferrerRefTestObj();
+
+ $referrerTestObj->id = "/functional/referrerTestObj";
+ $referrerTestObj->name = "referrer";
+ $referrerRefTestObj->id = "/functional/referrerRefTestObj";
+ $referrerRefTestObj->name = "referenced";
+
+ $referrerTestObj->reference = $referrerRefTestObj;
+
+ $this->dm->persist($referrerTestObj);
+ $this->dm->flush();
+ $this->dm->clear();
+
+
+
+ $reference = $this->dm->find($this->referrerType, "/functional/referrerRefTestObj");
+ $reference->referrers[0]->name = "referenced changed");
+
+ $this->dm->remove($reference);
+ $this->dm->flush();
+ $this->dm->clear();
+
+ $reference = $this->dm->find($this->referrerType, '/functional/referrerRefTestObj');
+ $this->assertNull($reference);
+
+ $referrer = $this->dm->find($this->referrerType, '/functional/referrerTestObj');
+ $this->assertEquals($referrer->name, 'referenced changed');
+ }
+}
+
+/**
+ * @PHPCRODM\Document(alias="ReferrerTestObj")
+ */
+class ReferrerTestObj
+{
+ /** @PHPCRODM\Id */
+ public $id;
+ /** @PHPCRODM\String */
+ public $name;
+ /** @PHPCRODM\ReferenceOne(targetDocument="ReferrerRefTestObj", weak=false) */
+ public $reference;
+}
+
+/**
+ * @PHPCRODM\Document(alias="ReferrerRefTestObj", referenceable="true")
+ */
+class ReferrerRefTestObj
+{
+ /** @PHPCRODM\Id */
+ public $id;
+ /** @PHPCRODM\String */
+ public $name;
+ /** @PHPCRODM\Referrers */
+ public $referrers;
+}

0 comments on commit 42c24a3

Please sign in to comment.
Something went wrong with that request. Please try again.