Skip to content

Commit

Permalink
Added Child annotation and File document
Browse files Browse the repository at this point in the history
Now you can include children in your documents by annotating them with
Child. The ODM will automatically create a child node in the repository
with the given name (if a document is assigned ...).
A useful example is the File document that has a child of class
Resource.
  • Loading branch information
Uwe Jäger committed Mar 20, 2011
1 parent d82f288 commit 09c5591
Show file tree
Hide file tree
Showing 12 changed files with 452 additions and 11 deletions.
15 changes: 15 additions & 0 deletions README.markdown
Expand Up @@ -22,6 +22,21 @@ Notes
Getting Started
---------------

0. Register custom node types in Jackrabbit

- stop your Jackrabbit instance
- run "java -jar jackrabbit-standalone-2.2.4.jar --cli file://<path to your repository>"
- enter "registernodetype <path to phpcr.cnd>"
- enter "quit"
- start your server again ...

where phpcr.cnd contains

<phpcr='http://www.doctrine-project.org/phpcr-odm'>
[phpcr:managed]
mixin
- phpcr:alias (STRING)

1. Define one of those mapping drivers

// Annotation driver
Expand Down
34 changes: 34 additions & 0 deletions lib/Doctrine/ODM/PHPCR/Document/File.php
@@ -0,0 +1,34 @@
<?php

namespace Doctrine\ODM\PHPCR\Document;

/**
* @phpcr:Document(alias="file", nodeType="nt:file")
*/
class File
{
/** @phpcr:Path */
protected $path;

/** @phpcr:Node */
protected $node;

/** @phpcr:Date(name="jcr:created") */
protected $created;

/** @phpcr:String(name="jcr:createdBy") */
protected $createdBy;

/** @phpcr:Child(name="jcr:content") */
protected $content;

public function setFileContent($filename)
{
if ($this->content === null)
{
$this->content = new Resource();
}
$this->content->setFileData(file_get_contents($filename));
}

}
23 changes: 23 additions & 0 deletions lib/Doctrine/ODM/PHPCR/Document/Folder.php
@@ -0,0 +1,23 @@
<?php

namespace Doctrine\ODM\PHPCR\Document;

/**
* @phpcr:Document(alias="folder", nodeType="nt:folder")
*/
class Folder
{
/** @phpcr:Path */
protected $path;

/** @phpcr:Node */
protected $node;

/** @phpcr:Date(name="jcr:created") */
protected $created;

/** @phpcr:String(name="jcr:createdBy") */
protected $createdBy;
}


37 changes: 37 additions & 0 deletions lib/Doctrine/ODM/PHPCR/Document/Resource.php
@@ -0,0 +1,37 @@
<?php

namespace Doctrine\ODM\PHPCR\Document;

/**
* @phpcr:Document(alias="resource", nodeType="nt:resource")
*/
class Resource
{
/** @phpcr:Path */
protected $path;

/** @phpcr:Node */
protected $node;

/** @phpcr:Binary(name="jcr:data") */
protected $data;

/** @phpcr:String(name="jcr:mimeType") */
protected $mimeType;

/** @phpcr:String(name="jcr:encoding") */
protected $encoding;

/** @phpcr:Date(name="jcr:lastModified") */
protected $lastModified;

/** @phpcr:String(name="jcr:lastModifiedBy") */
protected $lastModifiedBy;


public function setFileData($data)
{
$this->data = $data;
}
}

3 changes: 3 additions & 0 deletions lib/Doctrine/ODM/PHPCR/Mapping/Driver/AnnotationDriver.php
Expand Up @@ -140,6 +140,9 @@ public function loadMetadataForClass($className, ClassMetadata $class)
} elseif ($fieldAnnot instanceof \Doctrine\ODM\PHPCR\Mapping\Node) {
$mapping = array_merge($mapping, (array) $fieldAnnot);
$class->mapNode($mapping);
} elseif ($fieldAnnot instanceof \Doctrine\ODM\PHPCR\Mapping\Child) {
$mapping = array_merge($mapping, (array) $fieldAnnot);
$class->mapChild($mapping);
} elseif ($fieldAnnot instanceof \Doctrine\ODM\PHPCR\Mapping\ReferenceOne) {
$cascade = 0;
foreach ($fieldAnnot->cascade as $cascadeMode) {
Expand Down
4 changes: 4 additions & 0 deletions lib/Doctrine/ODM/PHPCR/Mapping/Driver/DoctrineAnnotations.php
Expand Up @@ -82,3 +82,7 @@ final class ReferenceMany extends Reference
public $cascade = array();
public $mappedBy;
}
class Child extends Annotation
{
public $name;
}
9 changes: 9 additions & 0 deletions lib/Doctrine/ODM/PHPCR/Mapping/Driver/XmlDriver.php
Expand Up @@ -89,6 +89,10 @@ public function loadMetadataForClass($className, ClassMetadata $class)
$mapping = array('fieldName' => (string) $xmlRoot->node->attributes()->name);
$this->addNodeMapping($class, $mapping);
}
if (isset($xmlRoot->child)) {
$mapping = array('fieldName' => (string) $xmlRoot->child->attributes()->name);
$this->addChildMapping($class, $mapping);
}
if (isset($xmlRoot->{'reference-many'})) {
foreach ($xmlRoot->{'reference-many'} as $reference) {
$this->addReferenceMapping($class, $reference, 'many');
Expand Down Expand Up @@ -116,6 +120,11 @@ private function addNodeMapping(ClassMetadata $class, $mapping)
$class->mapNode($mapping);
}

private function addChildMapping(ClassMetadata $class, $mapping)
{
$class->mapChild($mapping);
}

private function addReferenceMapping(ClassMetadata $class, $reference, $type)
{
$cascade = array_keys((array) $reference->cascade);
Expand Down
9 changes: 9 additions & 0 deletions lib/Doctrine/ODM/PHPCR/Mapping/Driver/YamlDriver.php
Expand Up @@ -87,6 +87,10 @@ public function loadMetadataForClass($className, ClassMetadata $class)
$mapping = array('fieldName' => $element['node']);
$this->addNodeMapping($class, $mapping);
}
if (isset($element['child'])) {
$mapping = array('fieldName' => $element['child']);
$this->addChildMapping($class, $mapping);
}
if (isset($element['referenceOne'])) {
foreach ($element['referenceOne'] as $fieldName => $reference) {
$this->addMappingFromReference($class, $fieldName, $reference, 'one');
Expand Down Expand Up @@ -114,6 +118,11 @@ private function addNodeMapping(ClassMetadata $class, $mapping)
$class->mapNode($mapping);
}

private function addChildMapping(ClassMetadata $class, $mapping)
{
$class->mapChild($mapping);
}

private function addMappingFromReference(ClassMetadata $class, $fieldName, $reference, $type)
{
$mapping = array(
Expand Down
64 changes: 55 additions & 9 deletions lib/Doctrine/ODM/PHPCR/UnitOfWork.php
Expand Up @@ -141,16 +141,16 @@ public function createDocument($documentName, $node, array &$hints = array())
{
// TODO create a doctrine: namespace and register node types with doctrine:name

if ($node->hasProperty('_doctrine_alias')) {
$metadata = $this->dm->getMetadataFactory()->getMetadataForAlias($node->getPropertyValue('_doctrine_alias'));
if ($node->hasProperty('phpcr:alias')) {
$metadata = $this->dm->getMetadataFactory()->getMetadataForAlias($node->getPropertyValue('phpcr:alias'));
$type = $metadata->name;
if (isset($documentName) && $this->dm->getConfiguration()->getValidateDoctrineMetadata()) {
$validate = true;
}
} else if (isset($documentName)) {
$type = $documentName;
if ($this->dm->getConfiguration()->getWriteDoctrineMetadata()) {
$node->setProperty('_doctrine_alias', $documentName);
$node->setProperty('phpcr:alias', $documentName);
}
} else {
throw new \InvalidArgumentException("Missing Doctrine metadata in the Document, cannot hydrate (yet)!");
Expand Down Expand Up @@ -228,6 +228,14 @@ public function createDocument($documentName, $node, array &$hints = array())
}
}

foreach ($class->childMappings as $childName => $mapping) {
if ($node->hasNode($mapping['name'])) {
$documentState[$class->childMappings[$childName]['fieldName']] = $this->createDocument(null, $node->getNode($mapping['name']));
} else {
$documentState[$class->childMappings[$childName]['fieldName']] = null;
}
}

if (isset($this->identityMap[$id])) {
$document = $this->identityMap[$id];
$overrideLocalValues = false;
Expand Down Expand Up @@ -317,7 +325,7 @@ private function doScheduleInsert($document, &$visited)
break;
}

$this->cascadeScheduleInsert($class, $document, $visited);
$this->cascadeScheduleInsert($class, $document, $visited, $path);
}

/**
Expand All @@ -326,7 +334,7 @@ private function doScheduleInsert($document, &$visited)
* @param object $document
* @param array $visited
*/
private function cascadeScheduleInsert($class, $document, &$visited)
private function cascadeScheduleInsert($class, $document, &$visited, $path)
{
foreach ($class->associationsMappings as $assocName => $assoc) {
if ( ($assoc['cascade'] & ClassMetadata::CASCADE_PERSIST) ) {
Expand All @@ -345,6 +353,12 @@ 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) {
$this->doScheduleInsert($child, $path.'/'.$mapping['name'], $visited);
}
}
}

private function getIdGenerator($type)
Expand All @@ -366,6 +380,28 @@ public function scheduleRemove($document)
}
}

private function purgeChildren($document)
{
$class = $this->dm->getClassMetadata(get_class($document));
foreach ($class->childMappings as $childName => $mapping) {
$child = $class->reflFields[$childName]->getValue($document);
if ($child !== null) {
$this->purgeChildren($child);

$oid = spl_object_hash($child);
unset(
$this->scheduledRemovals[$oid],
$this->scheduledInserts[$oid],
$this->scheduledUpdates[$oid]
);

$this->removeFromIdentityMap($child);
}
}


}

public function getDocumentState($document)
{
$oid = spl_object_hash($document);
Expand Down Expand Up @@ -434,7 +470,7 @@ public function computeChangeSet(ClassMetadata $class, $document)

$changed = false;
foreach ($actualData as $fieldName => $fieldValue) {
if (!isset($class->fieldMappings[$fieldName])) {
if (!isset($class->fieldMappings[$fieldName]) && !isset($class->childMappings[$fieldName])) {
continue;
}
if ($class->isCollectionValuedAssociation($fieldName)) {
Expand Down Expand Up @@ -517,6 +553,7 @@ public function flush()
foreach ($this->scheduledRemovals as $oid => $document) {
$this->nodesMap[$oid]->remove();
$this->removeFromIdentityMap($document);
$this->purgeChildren($document);
}

foreach ($this->scheduledInserts as $oid => $document) {
Expand All @@ -529,6 +566,7 @@ public function flush()
$path = $this->documentPaths[$oid];
$parentNode = $session->getNode(dirname($path) === '\\' ? '/' : dirname($path));
$node = $parentNode->addNode(basename($path), $class->nodeType);
$node->addMixin('phpcr:managed');

if ($class->isVersioned) {
$node->addMixin("mix:versionable");
Expand All @@ -542,7 +580,7 @@ public function flush()
$class->reflFields[$class->node]->setValue($document, $node);
}
if ($useDoctrineMetadata) {
$node->setProperty('_doctrine_alias', $class->alias, 'string');
$node->setProperty('phpcr:alias', $class->alias, 'string');
}

foreach ($this->documentChangesets[$oid] as $fieldName => $fieldValue) {
Expand All @@ -568,7 +606,7 @@ public function flush()
}

if ($useDoctrineMetadata) {
$node->setProperty('_doctrine_alias', $class->alias);
$node->setProperty('phpcr:alias', $class->alias);
}

foreach ($this->documentChangesets[$oid] as $fieldName => $fieldValue) {
Expand Down Expand Up @@ -600,8 +638,16 @@ public function flush()
$data['doctrine_metadata']['associations'][$fieldName] = $ids;
}
}
// child is set to null ... remove the node ...
} else if (isset($class->childMappings[$fieldName]) && $fieldValue === null) {
if ($node->hasNode($class->childMappings[$fieldName]['name'])) {
$child = $node->getNode($class->childMappings[$fieldName]['name']);
$childDocument = $this->createDocument(null, $child);
$this->purgeChildren($childDocument);
$child->remove();
}
}
}
}

// respect the non mapped data, otherwise they will be deleted.
if (isset($this->nonMappedData[$oid]) && $this->nonMappedData[$oid]) {
Expand Down

0 comments on commit 09c5591

Please sign in to comment.