Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Child annotation and file handling #4

Merged
merged 12 commits into from

3 participants

@uwej711

Here is a first version of a child annotation that allows for "embedded" documents. The name of the child is determined by the annotation. A simple example is the child jcr:content required by the nt:file node type.

I also added the custom mixin phpcr:managed and Document classes for File and Folder. For usage refer to ChildTest and FileTest in the functional tests directory.

Note: to be able to use the ODM with this patch it is necessary to register the node type phpcr:managed as described in the readme.

Any feedback is welcome.

Kind regards
Uwe

Uwe Jäger added some commits
Uwe Jäger Added Child annotation and File document
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.
09c5591
Uwe Jäger Rebased current master and clean up
From master id ggeneration was introduced. Made it work with current
changes.
3a97568
Uwe Jäger Exception when a child is replaced by another object
Throw an exception when someone tries to replace a child object with
another object.
9846982
lib/Doctrine/ODM/PHPCR/Id/IdGenerator.php
@@ -3,6 +3,7 @@
namespace Doctrine\ODM\PHPCR\Id;
use Doctrine\ODM\PHPCR\DocumentManager;
+use Doctrine\ODM\PHPCR\Mapping\ClassMetadata;
@lsmith77 Owner

i fixed this issue in #5

@uwej711
uwej711 added a note

The tests did not run :-)

@lsmith77 Owner

Ok, did a few fixes in the tests, now I am down to:
Tests: 69, Assertions: 101, Errors: 19, Incomplete: 2, Skipped: 2.

BTW by default the tests use a "tests" workspace that you need to manually create:
http://www.eppelheimer.com/clients/day/jackrabbit/site/apache/faq.html#create-workspace

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@lsmith77 lsmith77 commented on the diff
lib/Doctrine/ODM/PHPCR/Mapping/ClassMetadataInfo.php
@@ -179,6 +179,12 @@ class ClassMetadataInfo implements ClassMetadata
public $associationsMappings = array();
@lsmith77 Owner

FYI: In general i want to see the ClassMetadataInfo implementations of MongoDB, CouchDB and PHPCR ODM come together a bit more. Right now I prefer the way MongoDB does reference/embed the most.

@uwej711
uwej711 added a note

I didn't look at those but at JCR ... therefore it's named Child now ...
For the reference stuff I think we should also go the JCR way, i.e. have properties fo type Reference or WeakReference on the nodes, multivalued if it is a to-many association.

@lsmith77 Owner

imho in jackalope we should follow JCR/Jackrabbit naming, but in PHPCR ODM we should follow the naming of the other ODM's as much as possible.

@uwej711
uwej711 added a note

But what do we do if the concepts behind are different? A child in JCR is just a full-feature repository node as every other - it is visible from outside. Ther e is no notion of containment or anything else ...
Anyway I will look at the other ODMs to.

@lsmith77 Owner

IMHO we should focus PHPCR ODM on the things that are the same or similar. This is also why I am advocating with the renaming of path to id. The main point from my POV is to make it possible to share knowledge and code with the other ODM's. For most PHPCR specific stuff imho developers can fallback to Jackalope. However we can also of course add some connivence features to PHOCR ODM that are PHPCR specific.

So I guess the gist is that we should always carefully review if there are similar concepts on the other ODM's and if we can sensibly adopt the naming form there. Of course this is just my POV.

@uwej711
uwej711 added a note

Three last comments here (this should be part of my feedback on the list):

  • If the concepts are the same or similar it should be named the same way and work the same way

  • If it's all the same you loose the good reasons for using phpcr ...

  • The fallback to PHPCR is there but it basically breaks your controllers or services - somewhere you need to place the code. If not in the ODM it will move up ...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
lib/Doctrine/ODM/PHPCR/UnitOfWork.php
@@ -20,6 +20,7 @@
namespace Doctrine\ODM\PHPCR;
use Doctrine\ODM\PHPCR\Mapping\ClassMetadata;
+use Doctrine\ODM\PHPCR\Mapping\ClassMetadataInfo;
@lsmith77 Owner

looking at MongoDB and CouchDB ODM it looks like they only use ClassMetadataInfo for caching, in the rest of the code they just reference ClassMetadata.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@lsmith77
Owner

i will try to run the tests tonight .. i need to add tests for the id generator stuff too :-/

@lsmith77 lsmith77 commented on the diff
lib/Doctrine/ODM/PHPCR/UnitOfWork.php
@@ -141,8 +142,8 @@ class UnitOfWork
{
// TODO create a doctrine: namespace and register node types with doctrine:name
- if ($node->hasProperty('_doctrine_alias')) {
@lsmith77 Owner

i do not have a strong opinion about this change. actually i think its good. but its a significant change that we should consciously do and get right the first time around.

@uwej711
uwej711 added a note

if this is not done via a proper mixin you cannot use builtin jcr note types. So I think there is no way around doing this ...

@dbu Collaborator
dbu added a note

i really like the idea. its even better than the namespaced doctrine attribute property we originally had in mind.
it IS a fundamental change, but i am rather confident its a good one, so we should do it as early as possible :-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@dbu

is this needed? the call to phpcr Node::remove() does a recursive remove itself.
or do we have to give the child entity a chance to trigger something on application level?

When the child node to be removed has mapped children that are modified flush will try to create or save the nodes which will lead to an exception from Jackalope/Jackrabbit. This code just removes all the documents, it does not schedule the descendants for removal in the content repository. That is taken care of by the Node::remove(), as you say.

Collaborator

ok, good. maybe you could add this as a doc comment for future reference?

@dbu

please add a short doc comment what difference Remove1 and Remove2 have

@dbu

why is this not allowed? maybe i missed something on the mailinglist - but it would be good to have a comment right here for other people asking that same question. and some mention of the fact in the doc.

I gave my view on this on the thread about renaming path to id. I think "moving" nodes should be a separate operation. This will also keep the UnitOfWork a bit simpler. To support that fully we need to remove the old child and possibly move the new child. Maybe it's easier to implement than I first thought.

Collaborator

ah, right. and i agreed with you :-D
i just did not really see that this would be a move. maybe you could make the exception text more explicit: "You can not move or copy children by assignment as it would be ambigous. Please use the PHPCR\Session::move() resp PHPCR\Session::copy operations for this."

@dbu

really a detail: indention is not consistent for the above line

@dbu

have a doc comment for the setFileContent function? i suppose filename must be an absolute path, right?

would be nice to also have a method to set content with a string directly, so if i generate something in memory i do not have to first write it to a temporary file

This was created mainly as a first use case for the child annotation and I don't consider that finished. I would like to integrate it fully with forms to finish it.

Collaborator

i think it makes sense to have both a setFileContent($content) method and a setFileContentFromFilesystem($absolute_filename), and then another one for the form component if needed, once that component settled.

@dbu
Collaborator

hi uwe,
thanks a lot for the work you did. i am no doctrine expert, but this looks pretty good to me. i commented on some phpcr related issues. i think its an excellent idea to provide default entities for the built-in file and folder types.

open points to me are

  • is the annotation going to be called child or embed? as we pre-load, i think embed sounds more appropriate. and consistent with the other odm's. even though its not exactly the same as embed in mongodb...
  • we should have some doc comments on the entities pointing to the relevant jcr doc, and doc in other places where appropriate
  • what happens if there are multiple children with the same name? this is allowed by jcr and would result in the names being indexed with [0]... important for now is that the annotation can allow for this case later.
  • lazy loading - but this optimization, we can do that later
@dbu

trailing empty line

@dbu

why did you define the properties as protected? shouldn't they be public?

I think these should be basic classes that are subclassed by the application/bundle etc. I don't consider File, Folder and Resource completly finished for now. Therefore I started of with protected properties.

Collaborator

ok. although i think it can make sense to use them right away :-)
but either way, please explain in the doc comment what the intention is.

@uwej711

hi david,

regarding the same name siblings: I think this is more the case for a Children annotation where there is an array of documents as children. If I have two children with the same name I can not know which belongs to which property when recreating the document.

About the naming ... someone has to decide ...

Uwe Jäger Changed some minor issues as commented on the pull request
Added some comments and minor formatting stuff
a4b46b5
@dbu
Collaborator

about the name: i switch to mailing list for that discussion to hopefully get more input.

same name siblings: for later - there are bugs in jackalope with that anyway. one use case i see: i want to atttach an arbitrary number of pictures or download files to a node. but then, it is probably more clean to do that with a nt:folder and then the nt:files having individual names and get all children of nt:folder.

about the discussion about state of the code: you created a pull request, so i assumed you want your changes merged into the phpcr-odm codebase :-) and to merge it, we should have some comments and stuff, even if its going to change or be improved later on;:-)

@uwej711

Right - I walk through your comments and try to add the things you suggested.

@uwej711 uwej711 Cleaned up the code according to dbu's recommendation
Added comments and made File, Folder and Resource directly usable.
On test is failing due to a bug in UnitOfWork fixed in master.
5730595
@uwej711

OK, I added some comments and changed the File and Folder classes to be usable directly. So there seems to be only the naming stuff left ...

@dbu
Collaborator
dbu commented

hi uwe,
we are getting there :-) with the naming, things should be clear, right?
to merge this pull, the piece missing is a symfony command to wrap the register node command in phpcr-odm (just merged your pull request there). and maybe some sort of check if the node type has been registered. or just capture the exception about the missing type and provide a dev friendly exception explaining he has first to run the command to register the mixin type.
cheers,david

@uwej711

name is child ... :-)

catching the exception is a bit difficult, since there is no nice exception thrown by Jackalope or Jackrabbit on Node::addMixin (was the case last time I checked - will look at that again). According to the JCR-Spec a NoSuchNodeTypeException should be thrown.

@uwej711

@dbu: Regarding the exception: the jackrabbit java client checks the node types existence when Node:addMixin is called. The jackrabbit server does no check on Session:save. The spec leaves this to the implementation so to be on the save side we should implement the check in Jackalope Node:addMixin.

@dbu
Collaborator

you are right. as a first step, i will try to make jackalope at least throw the correct exception on save.

uwej711 added some commits
@uwej711 uwej711 Merge remote-tracking branch 'doctrine/master' into child_annotation 9e75f54
@uwej711 uwej711 Removed Id from fieldMapping
Since the Id is used internally as the nodes path it is not necessary to
store it with the node - it is actually impossible to do it with a
predefined node type that does not allow arbitrary properties as
nt:file. There for we need a different mapping for Id (as it was before
with Path).
89dcc11
@uwej711

I merged doctrine/master. The renaming of path to id introduced a problem since the id was also stored as a property on the node. This is fixed with 89dcc11.

@dbu
Collaborator

oh, good catch. and it totally does not make sense to store the id with the node if its the nodes path.

lib/Doctrine/ODM/PHPCR/Document/File.php
((85 lines not shown))
+ *
+ * @param string $content the content for the file
+ */
+ public function setFileContent($content)
+ {
+ $this->getContent();
+ $this->content->setData($content);
+ }
+
+ /*
+ * Ensure content object is created
+ */
+ private function getContent()
+ {
+ if ($this->content === null)
+ {
@lsmith77 Owner
lsmith77 added a note

this { should be on the previous line

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
lib/Doctrine/ODM/PHPCR/UnitOfWork.php
((18 lines not shown))
}
- }
+ }
@lsmith77 Owner
lsmith77 added a note

no need to add trailing whitespace :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
lib/Doctrine/ODM/PHPCR/UnitOfWork.php
@@ -618,8 +665,22 @@ class UnitOfWork
$data['doctrine_metadata']['associations'][$fieldName] = $ids;
}
}
+ // child is set to null ... remove the node ...
+ } else if (isset($class->childMappings[$fieldName])) {
@lsmith77 Owner
lsmith77 added a note

PEAR/Symfony2 CS defines that it should be "elseif", but we might need to clean this up in lots of places

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@dbu dbu merged commit 0fdab3c into doctrine:master
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Mar 20, 2011
  1. Added Child annotation and File document

    Uwe Jäger authored
    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.
  2. Rebased current master and clean up

    Uwe Jäger authored
    From master id ggeneration was introduced. Made it work with current
    changes.
  3. Exception when a child is replaced by another object

    Uwe Jäger authored
    Throw an exception when someone tries to replace a child object with
    another object.
Commits on Mar 21, 2011
  1. Changed some minor issues as commented on the pull request

    Uwe Jäger authored
    Added some comments and minor formatting stuff
Commits on Mar 22, 2011
  1. @uwej711

    Cleaned up the code according to dbu's recommendation

    uwej711 authored
    Added comments and made File, Folder and Resource directly usable.
    On test is failing due to a bug in UnitOfWork fixed in master.
Commits on Apr 13, 2011
  1. @uwej711
  2. @uwej711

    Removed Id from fieldMapping

    uwej711 authored
    Since the Id is used internally as the nodes path it is not necessary to
    store it with the node - it is actually impossible to do it with a
    predefined node type that does not allow arbitrary properties as
    nt:file. There for we need a different mapping for Id (as it was before
    with Path).
Commits on May 9, 2011
  1. @uwej711
  2. @uwej711

    Integrated latest Jackalope changes

    uwej711 authored
    Also made tests running and fix some CS
  3. @uwej711
  4. @uwej711

    Add a command to register system node types

    uwej711 authored
    registers the node type phpcr:managed without havin to create the file
  5. @uwej711

    change namespace url

    uwej711 authored
This page is out of date. Refresh to see the latest.
Showing with 954 additions and 21 deletions.
  1. +15 −0 README.markdown
  2. +129 −0 lib/Doctrine/ODM/PHPCR/Document/File.php
  3. +67 −0 lib/Doctrine/ODM/PHPCR/Document/Folder.php
  4. +144 −0 lib/Doctrine/ODM/PHPCR/Document/Resource.php
  5. +28 −1 lib/Doctrine/ODM/PHPCR/Mapping/ClassMetadataInfo.php
  6. +4 −1 lib/Doctrine/ODM/PHPCR/Mapping/Driver/AnnotationDriver.php
  7. +5 −1 lib/Doctrine/ODM/PHPCR/Mapping/Driver/DoctrineAnnotations.php
  8. +15 −1 lib/Doctrine/ODM/PHPCR/Mapping/Driver/XmlDriver.php
  9. +15 −1 lib/Doctrine/ODM/PHPCR/Mapping/Driver/YamlDriver.php
  10. +69 −0 lib/Doctrine/ODM/PHPCR/Tools/Console/Command/RegisterSystemNodeTypesCommand.php
  11. +75 −10 lib/Doctrine/ODM/PHPCR/UnitOfWork.php
  12. +1 −1  lib/vendor/jackalope
  13. +1 −1  tests/Doctrine/Tests/ODM/PHPCR/Functional/BasicCrudTest.php
  14. +236 −0 tests/Doctrine/Tests/ODM/PHPCR/Functional/ChildTest.php
  15. +96 −0 tests/Doctrine/Tests/ODM/PHPCR/Functional/FileTest.php
  16. +48 −0 tests/Doctrine/Tests/ODM/PHPCR/Functional/FolderTest.php
  17. +1 −2  tests/Doctrine/Tests/ODM/PHPCR/Mapping/AbstractMappingDriverTest.php
  18. +4 −1 tests/Doctrine/Tests/ODM/PHPCR/PHPCRFunctionalTestCase.php
  19. +1 −1  tests/Doctrine/Tests/ODM/PHPCR/Performance/InsertPerformanceTest.php
View
15 README.markdown
@@ -24,6 +24,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
View
129 lib/Doctrine/ODM/PHPCR/Document/File.php
@@ -0,0 +1,129 @@
+<?php
+
+namespace Doctrine\ODM\PHPCR\Document;
+
+/**
+ * This class represents a JCR file, aka nt:file.
+ * @see http://wiki.apache.org/jackrabbit/nt:file
+ *
+ * @phpcr:Document(alias="file", nodeType="nt:file")
+ */
+class File
+{
+ /** @phpcr:Id */
+ protected $id;
+
+ /** @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;
+
+ /**
+ * setter for id
+ *
+ * @param string $id of the node
+ */
+ public function setId($id)
+ {
+ $this->id = $id;
+ }
+
+ /**
+ * getter for id
+ *
+ * @return string id of the node
+ */
+ public function getId()
+ {
+ return $this->id;
+ }
+
+ /**
+ * getter for created
+ * The created date is assigned by the content repository
+ *
+ * @return DateTime created date of the file
+ */
+ public function getCreated()
+ {
+ return $this->created;
+ }
+
+ /**
+ * getter for createdBy
+ * The createdBy is assigned by the content repository
+ * This is the name of the (jcr) user that created the node
+ *
+ * @return string name of the (jcr) user who created the file
+ */
+ public function getCreatedBy()
+ {
+ return $this->createdBy;
+ }
+
+ /**
+ * Set the content for this file from the given filename.
+ * Calls file_get_contents with the given filename
+ *
+ * @param string $filename name of the file which contents should be used
+ */
+ public function setFileContentFromFilesystem($filename)
+ {
+ $this->getContent();
+ $stream = fopen($filename, 'rb');
+ $this->content->setData($stream);
+ }
+
+ /**
+ * Set the content for this file from the given string.
+ *
+ * @param string $content the content for the file
+ */
+ public function setFileContent($content)
+ {
+ $this->getContent();
+ $stream = fopen('php://memory', 'rwb+');
+ fwrite($stream, $content);
+ rewind($stream);
+ $this->content->setData($stream);
+ }
+
+ /**
+ * Get a stream for the content of this file.
+ *
+ * @return stream the content for the file
+ */
+ public function getFileContentAsStream()
+ {
+ return $this->getContent()->getData();
+ }
+
+ /**
+ * Get the content for this file as string.
+ *
+ * @return string the content for the file in a string
+ */
+ public function getFileContent()
+ {
+ $content = stream_get_contents($this->getContent()->getData());
+ return $content !== false ? $content : '';
+ }
+
+ /*
+ * Ensure content object is created
+ */
+ private function getContent()
+ {
+ if ($this->content === null) {
+ $this->content = new Resource();
+ }
+ return $this->content;
+ }
+}
View
67 lib/Doctrine/ODM/PHPCR/Document/Folder.php
@@ -0,0 +1,67 @@
+<?php
+
+namespace Doctrine\ODM\PHPCR\Document;
+
+/**
+ * This class represents a Folder in the repository, aka nt:folder
+ * @see http://wiki.apache.org/jackrabbit/nt:folder
+ *
+ * @phpcr:Document(alias="folder", nodeType="nt:folder")
+ */
+class Folder
+{
+ /** @phpcr:Id */
+ protected $id;
+
+ /** @phpcr:Node */
+ protected $node;
+
+ /** @phpcr:Date(name="jcr:created") */
+ protected $created;
+
+ /** @phpcr:String(name="jcr:createdBy") */
+ protected $createdBy;
+
+ /**
+ * setter for id
+ *
+ * @param string $id of the node
+ */
+ public function setId($id)
+ {
+ $this->id = $id;
+ }
+
+ /**
+ * getter for id
+ *
+ * @return string id of the node
+ */
+ public function getId()
+ {
+ return $this->id;
+ }
+
+ /**
+ * getter for created
+ * The created date is assigned by the content repository
+ *
+ * @return DateTime created date of the file
+ */
+ public function getCreated()
+ {
+ return $this->created;
+ }
+
+ /**
+ * getter for createdBy
+ * The createdBy is assigned by the content repository
+ * This is the name of the (jcr) user that created the node
+ *
+ * @return string name of the (jcr) user who created the file
+ */
+ public function getCreatedBy()
+ {
+ return $this->createdBy;
+ }
+}
View
144 lib/Doctrine/ODM/PHPCR/Document/Resource.php
@@ -0,0 +1,144 @@
+<?php
+
+namespace Doctrine\ODM\PHPCR\Document;
+
+/**
+ * This class represents a jcr nt:resource and is used by the File document
+ * @see http://wiki.apache.org/jackrabbit/nt:resource
+ *
+ * @phpcr:Document(alias="resource", nodeType="nt:resource")
+ */
+class Resource
+{
+ /** @phpcr:Id */
+ protected $id;
+
+ /** @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;
+
+ /**
+ * setter for the data property
+ * This property stores the content of this resource
+ *
+ * @param stream $data the contents of this resource
+ */
+ public function setData($data)
+ {
+ $this->data = $data;
+ }
+
+ /**
+ * getter for the data property
+ * This returns the content of this resource
+ *
+ * @param stream
+ */
+ public function getData()
+ {
+ return $this->data;
+ }
+
+ /**
+ * setter for the mimeType property
+ * This property stores the mimeType of this resource
+ *
+ * @param string $mimeType
+ */
+ public function setMimeType($mimeType)
+ {
+ $this->mimeType = $mimeType;
+ }
+
+ /**
+ * getter for the mimeType property
+ * This returns the mimeType of this resource
+ *
+ * @return string
+ */
+ public function getMimeType()
+ {
+ return $this->mimeType;
+ }
+
+ /**
+ * setter for the encoding property
+ * This property stores the encoding of this resource
+ *
+ * @param string $encoding
+ */
+ public function setEncoding($encoding)
+ {
+ $this->encoding = $encoding;
+ }
+
+ /**
+ * getter for the encoding property
+ * This returns the encoding of this resource
+ *
+ * @return string
+ */
+ public function getEncoding()
+ {
+ return $this->encoding;
+ }
+
+ /**
+ * setter for the lastModified property
+ * This property stores the lastModified date of this resource
+ * If not set, this might be set by PHPCR
+ *
+ * @param DateTime $lastModified
+ */
+ public function setLastModified($lastModified)
+ {
+ $this->lastModified = $lastModified;
+ }
+
+ /**
+ * getter for the lastModified property
+ * This returns the lastModified date of this resource
+ *
+ * @return DateTime
+ */
+ public function getLastModified()
+ {
+ return $this->lastModified;
+ }
+
+ /**
+ * setter for the lastModifiedBy property
+ * name of the jcr user that last modified this resource
+ *
+ * @param string $lastModifiedBy
+ */
+ public function setLastModifiedBy($lastModifiedBy)
+ {
+ $this->lastModifiedBy = $lastModifiedBy;
+ }
+
+ /**
+ * getter for the lastModifiedBy property
+ * This returns name of the jcr user that last modified this resource
+ *
+ * @return string
+ */
+ public function getLastModifiedBy()
+ {
+ return $this->lastModifiedBy;
+ }
+}
View
29 lib/Doctrine/ODM/PHPCR/Mapping/ClassMetadataInfo.php
@@ -180,6 +180,12 @@ class ClassMetadataInfo implements ClassMetadata
public $associationsMappings = array();
@lsmith77 Owner

FYI: In general i want to see the ClassMetadataInfo implementations of MongoDB, CouchDB and PHPCR ODM come together a bit more. Right now I prefer the way MongoDB does reference/embed the most.

@uwej711
uwej711 added a note

I didn't look at those but at JCR ... therefore it's named Child now ...
For the reference stuff I think we should also go the JCR way, i.e. have properties fo type Reference or WeakReference on the nodes, multivalued if it is a to-many association.

@lsmith77 Owner

imho in jackalope we should follow JCR/Jackrabbit naming, but in PHPCR ODM we should follow the naming of the other ODM's as much as possible.

@uwej711
uwej711 added a note

But what do we do if the concepts behind are different? A child in JCR is just a full-feature repository node as every other - it is visible from outside. Ther e is no notion of containment or anything else ...
Anyway I will look at the other ODMs to.

@lsmith77 Owner

IMHO we should focus PHPCR ODM on the things that are the same or similar. This is also why I am advocating with the renaming of path to id. The main point from my POV is to make it possible to share knowledge and code with the other ODM's. For most PHPCR specific stuff imho developers can fallback to Jackalope. However we can also of course add some connivence features to PHOCR ODM that are PHPCR specific.

So I guess the gist is that we should always carefully review if there are similar concepts on the other ODM's and if we can sensibly adopt the naming form there. Of course this is just my POV.

@uwej711
uwej711 added a note

Three last comments here (this should be part of my feedback on the list):

  • If the concepts are the same or similar it should be named the same way and work the same way

  • If it's all the same you loose the good reasons for using phpcr ...

  • The fallback to PHPCR is there but it basically breaks your controllers or services - somewhere you need to place the code. If not in the ODM it will move up ...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+
+ /**
+ * Mapping of child doucments that are child nodes in the repository
+ */
+ public $childMappings = array();
+
/**
* PHPCR documents are always versioned, this flag determines if this version is exposed to the userland.
*
@@ -478,6 +484,18 @@ public function mapField(array $mapping)
$this->fieldMappings[$mapping['fieldName']] = $mapping;
}
+ public function mapId(array $mapping)
+ {
+ if (isset($mapping['id']) && $mapping['id'] === true) {
+ $mapping['type'] = 'string';
+ $this->setIdentifier($mapping['fieldName']);
+ if (isset($mapping['strategy'])) {
+ $this->idGenerator = constant('Doctrine\ODM\PHPCR\Mapping\ClassMetadata::GENERATOR_TYPE_' . strtoupper($mapping['strategy']));
+ }
+ }
+ $this->validateAndCompleteFieldMapping($mapping, false);
+ }
+
public function mapNode(array $mapping)
{
$this->validateAndCompleteFieldMapping($mapping, false);
@@ -485,6 +503,15 @@ public function mapNode(array $mapping)
$this->node = $mapping['fieldName'];
}
+ public function mapChild(array $mapping)
+ {
+ $mapping = $this->validateAndCompleteFieldMapping($mapping, false);
+ if (!isset($mapping['name'])) {
+ $mapping['name'] = $mapping['fieldName'];
+ }
+ $this->childMappings[$mapping['fieldName']] = $mapping;
+ }
+
protected function validateAndCompleteFieldMapping($mapping, $isField = true)
{
if (!isset($mapping['fieldName'])) {
@@ -493,7 +520,7 @@ protected function validateAndCompleteFieldMapping($mapping, $isField = true)
if ($isField && !isset($mapping['name'])) {
$mapping['name'] = $mapping['fieldName'];
}
- if (isset($this->fieldMappings[$mapping['fieldName']]) || isset($this->associationsMappings[$mapping['fieldName']])) {
+ if (isset($this->fieldMappings[$mapping['fieldName']]) || isset($this->associationsMappings[$mapping['fieldName']]) || isset($this->childMappings[$mapping['fieldName']])) {
throw MappingException::duplicateFieldMapping($this->name, $mapping['fieldName']);
}
if ($isField && !isset($mapping['type'])) {
View
5 lib/Doctrine/ODM/PHPCR/Mapping/Driver/AnnotationDriver.php
@@ -136,10 +136,13 @@ public function loadMetadataForClass($className, ClassMetadata $class)
$class->mapField($mapping);
} elseif ($fieldAnnot instanceof \Doctrine\ODM\PHPCR\Mapping\Id) {
$mapping = array_merge($mapping, (array) $fieldAnnot);
- $class->mapField($mapping);
+ $class->mapId($mapping);
} 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) {
View
6 lib/Doctrine/ODM/PHPCR/Mapping/Driver/DoctrineAnnotations.php
@@ -23,7 +23,7 @@ class Property extends Annotation
public $type = 'undefined';
public $multivalue = false;
}
-final class Id extends Property
+final class Id extends Annotation
{
public $id = true;
public $type = 'string';
@@ -85,6 +85,10 @@ class Reference extends Annotation
public $cascade = array();
public $mappedBy;
}
+class Child extends Annotation
+{
+ public $name;
+}
/* Annotations for lifecycle callbacks */
final class HasLifecycleCallbacks extends Annotation {}
View
16 lib/Doctrine/ODM/PHPCR/Mapping/Driver/XmlDriver.php
@@ -83,12 +83,16 @@ public function loadMetadataForClass($className, ClassMetadata $class)
}
if (isset($xmlRoot->id)) {
$mapping = array('fieldName' => (string) $xmlRoot->id->attributes()->name, 'id' => true);
- $this->addFieldMapping($class, $mapping);
+ $this->addIdMapping($class, $mapping);
}
if (isset($xmlRoot->node)) {
$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');
@@ -108,6 +112,11 @@ public function loadMetadataForClass($className, ClassMetadata $class)
}
+ private function addIdMapping(ClassMetadata $class, $mapping)
+ {
+ $class->mapId($mapping);
+ }
+
private function addFieldMapping(ClassMetadata $class, $mapping)
{
$class->mapField($mapping);
@@ -118,6 +127,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);
View
16 lib/Doctrine/ODM/PHPCR/Mapping/Driver/YamlDriver.php
@@ -81,12 +81,16 @@ public function loadMetadataForClass($className, ClassMetadata $class)
}
if (isset($element['id'])) {
$mapping = array('fieldName' => $element['id'], 'id' => true);
- $this->addFieldMapping($class, $mapping);
+ $this->addIdMapping($class, $mapping);
}
if (isset($element['node'])) {
$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');
@@ -107,6 +111,11 @@ public function loadMetadataForClass($className, ClassMetadata $class)
}
+ private function addIdMapping(ClassMetadata $class, $mapping)
+ {
+ $class->mapId($mapping);
+ }
+
private function addFieldMapping(ClassMetadata $class, $mapping)
{
$class->mapField($mapping);
@@ -117,6 +126,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(
View
69 lib/Doctrine/ODM/PHPCR/Tools/Console/Command/RegisterSystemNodeTypesCommand.php
@@ -0,0 +1,69 @@
+<?php
+
+namespace Doctrine\ODM\PHPCR\Tools\Console\Command;
+
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+/**
+ * Command to register system node types.
+ *
+ * This command registers the necessary node types to get phpcr odm working
+ */
+
+class RegisterSystemNodeTypesCommand extends Command
+{
+ /**
+ * @see Console\Command\Command
+ */
+ protected function configure()
+ {
+ $this
+ ->setName('odm:phpcr:register-system-node-types')
+ ->setDescription('Register system node types in the PHPCR repository')
+ ->setDefinition(array(
+ new InputOption('allow-update', '', InputOption::VALUE_NONE, 'Overwrite existig node types'),
+ ))
+ ->setHelp(<<<EOT
+Register system node types in the PHPCR repository.
+
+This command registers the node types necessary for the ODM to work.
+
+If you use --allow-update existing node type definitions will be overwritten
+in the repository.
+EOT
+ );
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ $dm = $this->getHelper('dm')->getDocumentManager();
+
+ $cnd = <<<CND
+<phpcr='http://www.doctrine-project.org/projects/phpcr_odm'>
+[phpcr:managed]
+ mixin
+ - phpcr:alias (STRING)
+CND
+ ;
+ $allowUpdate = $input->getOption('allow-update');
+
+ $session = $dm->getPhpcrSession();
+ $ntm = $session->getWorkspace()->getNodeTypeManager();
+
+ try {
+ $ntm->registerNodeTypesCnd($cnd, $allowUpdate);
+ } catch (\PHPCR\NodeType\NodeTypeExistsException $e) {
+ if (!$allowUpdate) {
+ $output->write(PHP_EOL.'<error>The node type(s) you tried to register already exist.</error>'.PHP_EOL);
+ $output->write(PHP_EOL.'If you want to override the existing definition call this command with the ');
+ $output->write('<info>--allow-update</info> option.'.PHP_EOL);
+ }
+ throw $e;
+ }
+ $output->write(PHP_EOL.sprintf('Sucessfully registered system node types.') . PHP_EOL);
+ }
+}
View
85 lib/Doctrine/ODM/PHPCR/UnitOfWork.php
@@ -141,8 +141,8 @@ 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')) {
@lsmith77 Owner

i do not have a strong opinion about this change. actually i think its good. but its a significant change that we should consciously do and get right the first time around.

@uwej711
uwej711 added a note

if this is not done via a proper mixin you cannot use builtin jcr note types. So I think there is no way around doing this ...

@dbu Collaborator
dbu added a note

i really like the idea. its even better than the namespaced doctrine attribute property we originally had in mind.
it IS a fundamental change, but i am rather confident its a good one, so we should do it as early as possible :-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
- $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;
@@ -150,7 +150,7 @@ public function createDocument($documentName, $node, array &$hints = array())
} else if (isset($documentName)) {
$type = $documentName;
if ($this->dm->getConfiguration()->getWriteDoctrineMetadata()) {
- $node->setProperty('_doctrine_alias', $documentName, PropertyType::STRING);
+ $node->setProperty('phpcr:alias', $documentName, PropertyType::STRING);
}
} else {
throw new \InvalidArgumentException("Missing Doctrine metadata in the Document, cannot hydrate (yet)!");
@@ -226,6 +226,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;
@@ -291,7 +299,7 @@ public function scheduleInsert($document)
$this->doScheduleInsert($document, $visited);
}
- private function doScheduleInsert($document, &$visited)
+ private function doScheduleInsert($document, &$visited, $overrideIdGenerator = null)
{
$oid = spl_object_hash($document);
if (isset($visited[$oid])) {
@@ -304,7 +312,7 @@ private function doScheduleInsert($document, &$visited)
switch ($state) {
case self::STATE_NEW:
- $this->persistNew($class, $document);
+ $this->persistNew($class, $document, $overrideIdGenerator);
break;
case self::STATE_MANAGED:
// TODO: Change Tracking Deferred Explicit
@@ -347,6 +355,15 @@ 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) {
+ $childClass = $this->dm->getClassMetadata(get_class($child));
+ $id = $class->reflFields[$class->identifier]->getValue($document);
+ $childClass->reflFields[$childClass->identifier]->setValue($child , $id . '/'. $mapping['name']);
+ $this->doScheduleInsert($child, $visited, ClassMetadata::GENERATOR_TYPE_ASSIGNED);
+ }
+ }
}
private function getIdGenerator($type)
@@ -372,6 +389,33 @@ public function scheduleRemove($document)
}
}
+ /**
+ * recurse over all known child documents to remove them form this unit of work
+ * as their parent gets removed from phpcr. If you do not, flush will try to create
+ + orphaned nodes if these documents are modified which leads to a PHPCR exception
+ */
+ 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);
@@ -440,7 +484,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)) {
@@ -490,9 +534,9 @@ public function getDocumentChangeSet($document)
* @param object $document
* @return void
*/
- public function persistNew($class, $document)
+ public function persistNew($class, $document, $overrideIdGenerator = null)
{
- $id = $this->getIdGenerator($class->idGenerator)->generate($document, $class, $this->dm);
+ $id = $this->getIdGenerator($overrideIdGenerator ? $overrideIdGenerator : $class->idGenerator)->generate($document, $class, $this->dm);
$this->registerManaged($document, $id, null);
@@ -507,6 +551,7 @@ public function persistNew($class, $document)
/**
* Flush Operation - Write all dirty entries to the PHPCR.
*
+ * @throws PHPCRException if it detects that a existing child should be replaced
* @return void
*/
public function flush()
@@ -526,6 +571,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) {
@@ -544,6 +590,11 @@ public function flush()
$id = $this->documentIds[$oid];
$parentNode = $session->getNode(dirname($id) === '\\' ? '/' : dirname($id));
$node = $parentNode->addNode(basename($id), $class->nodeType);
+ try {
+ $node->addMixin('phpcr:managed');
+ } catch (\PHPCR\NodeType\NoSuchNodeTypeException $e) {
+ throw new PHPCRException("You need to register the node type phpcr:managed first. See https://github.com/doctrine/phpcr-odm/wiki/Custom-node-type-phpcr:managed");
+ }
if ($class->isVersioned) {
$node->addMixin("mix:versionable");
@@ -557,7 +608,7 @@ public function flush()
$class->reflFields[$class->node]->setValue($document, $node);
}
if ($useDoctrineMetadata) {
- $node->setProperty('_doctrine_alias', $class->alias, PropertyType::STRING);
+ $node->setProperty('phpcr:alias', $class->alias, PropertyType::STRING);
}
foreach ($this->documentChangesets[$oid] as $fieldName => $fieldValue) {
@@ -587,7 +638,7 @@ public function flush()
}
if ($useDoctrineMetadata) {
- $node->setProperty('_doctrine_alias', $class->alias);
+ $node->setProperty('phpcr:alias', $class->alias, PropertyType::STRING);
}
foreach ($this->documentChangesets[$oid] as $fieldName => $fieldValue) {
@@ -619,6 +670,20 @@ public function flush()
$data['doctrine_metadata']['associations'][$fieldName] = $ids;
}
}
+ // child is set to null ... remove the node ...
+ } elseif (isset($class->childMappings[$fieldName])) {
+ if ($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();
+ }
+ } else if (isset($this->originalData[$oid][$fieldName])) {
+ // this is currently not implemented
+ // the old child needs to be removed and the new child might be moved
+ throw new PHPCRException("You can not move or copy children by assignment as it would be ambigous. Please use the PHPCR\Session::move() resp PHPCR\Session::copy operations for this.");
+ }
}
}
2  lib/vendor/jackalope
@@ -1 +1 @@
-Subproject commit 3e7a6776cdeee5ab3109d4301b2a8a2d5e96af7d
+Subproject commit 10d4bf2c7a4e52bf1293c5873102c8cfe5e4d6f1
View
2  tests/Doctrine/Tests/ODM/PHPCR/Functional/BasicCrudTest.php
@@ -28,7 +28,7 @@ public function setUp()
$user = $this->node->addNode('user');
$user->setProperty('username', 'lsmith');
$user->setProperty('numbers', array(3, 1, 2));
- $user->setProperty('_doctrine_alias', 'user');
+ $user->setProperty('phpcr:alias', 'user', \PHPCR\PropertyType::STRING);
$this->dm->getPhpcrSession()->save();
}
View
236 tests/Doctrine/Tests/ODM/PHPCR/Functional/ChildTest.php
@@ -0,0 +1,236 @@
+<?php
+
+namespace Doctrine\Tests\ODM\PHPCR\Functional;
+
+/**
+ * @group functional
+ */
+class ChildTest extends \Doctrine\Tests\ODM\PHPCR\PHPCRFunctionalTestCase
+{
+ /**
+ * @var DocumentManager
+ */
+ private $dm;
+
+ private $type;
+ private $childType;
+
+ private $node;
+
+ public function setUp()
+ {
+ $this->type = 'Doctrine\Tests\ODM\PHPCR\Functional\ChildTestObj';
+ $this->childType = 'Doctrine\Tests\ODM\PHPCR\Functional\ChildChildTestObj';
+ $this->dm = $this->createDocumentManager();
+ $this->node = $this->resetFunctionalNode($this->dm);
+ }
+
+ public function testCreate()
+ {
+ $parent = new ChildTestObj();
+ $child = new ChildChildTestObj();
+ $parent->name = 'Parent';
+ $parent->child = $child;
+ $child->name = 'Child';
+ $parent->id = '/functional/childtest';
+
+ $this->dm->persist($parent);
+ $this->dm->flush();
+ $this->dm->clear();
+
+ $this->assertTrue($this->node->getNode('childtest')->hasNode('test'));
+ $this->assertEquals($this->node->getNode('childtest')->getNode('test')->getProperty('name')->getString(), 'Child');
+ }
+
+ public function testCreateWithoutChild()
+ {
+ $parent = new ChildTestObj();
+ $parent->name = 'Parent';
+ $parent->id = '/functional/childtest';
+
+ $this->dm->persist($parent);
+ $this->dm->flush();
+ $this->dm->clear();
+
+ $this->assertFalse($this->node->getNode('childtest')->hasNode('test'));
+ }
+
+ public function testCreateAddChildLater()
+ {
+ $parent = new ChildTestObj();
+ $parent->name = 'Parent';
+ $parent->id = '/functional/childtest';
+
+ $this->dm->persist($parent);
+ $this->dm->flush();
+ $this->dm->clear();
+
+ $this->assertFalse($this->node->getNode('childtest')->hasNode('test'));
+
+ $parent = $this->dm->find($this->type, '/functional/childtest');
+ $parent->child = new ChildChildTestObj();
+ $parent->child->name = 'Child';
+ $this->dm->persist($parent);
+
+ $this->dm->flush();
+ $this->dm->clear();
+
+ $this->assertTrue($this->node->getNode('childtest')->hasNode('test'));
+ $this->assertEquals($this->node->getNode('childtest')->getNode('test')->getProperty('name')->getString(), 'Child');
+
+ }
+
+ public function testUpdate()
+ {
+ $parent = new ChildTestObj();
+ $child = new ChildChildTestObj();
+ $parent->name = 'Parent';
+ $parent->id = '/functional/childtest';
+ $parent->child = $child;
+ $child->name = 'Child';
+
+ $this->dm->persist($parent);
+ $this->dm->flush();
+ $this->dm->clear();
+
+ $parent = $this->dm->find($this->type, '/functional/childtest');
+ $parent->child->name = 'Child changed';
+
+ $this->dm->persist($parent);
+ $this->dm->flush();
+ $this->dm->clear();
+ $this->assertNotEquals($this->node->getNode('childtest')->getNode('test')->getProperty('name')->getString(), 'Child');
+ $this->assertEquals($this->node->getNode('childtest')->getNode('test')->getProperty('name')->getString(), 'Child changed');
+ }
+
+ /**
+ * Remove the parent node, even when children are modified.
+ */
+ public function testRemove1()
+ {
+ $parent = new ChildTestObj();
+ $child = new ChildChildTestObj();
+ $parent->name = 'Parent';
+ $parent->id = '/functional/childtest';
+ $parent->child = $child;
+ $child->name = 'Child';
+
+ $this->dm->persist($parent);
+ $this->dm->flush();
+ $this->dm->clear();
+
+ $parent = $this->dm->find($this->type, '/functional/childtest');
+ $parent->child->name = 'Child changed';
+
+ $this->dm->remove($parent);
+ $this->dm->flush();
+ $this->dm->clear();
+ $parent = $this->dm->find($this->type, '/functional/childtest');
+ $this->assertNull($parent);
+ }
+
+ /**
+ * Remove the child, check that parent->child is not set afterwards
+ */
+ public function testRemove2()
+ {
+ $parent = new ChildTestObj();
+ $child = new ChildChildTestObj();
+ $parent->name = 'Parent';
+ $parent->id = '/functional/childtest';
+ $parent->child = $child;
+ $child->name = 'Child';
+
+ $this->dm->persist($parent);
+ $this->dm->flush();
+ $this->dm->clear();
+
+ $child = $this->dm->find($this->childType, '/functional/childtest/test');
+
+ $this->dm->remove($child);
+ $this->dm->flush();
+ $this->dm->clear();
+
+ $parent = $this->dm->find($this->type, '/functional/childtest');
+
+ $this->assertNull($parent->child);
+ $this->assertTrue($this->node->hasNode('childtest'));
+ $this->assertFalse($this->node->getNode('childtest')->hasNode('test'));
+ }
+
+ public function testChildSetNull()
+ {
+ $parent = new ChildTestObj();
+ $child = new ChildChildTestObj();
+ $parent->name = 'Parent';
+ $parent->id = '/functional/childtest';
+ $parent->child = $child;
+ $child->name = 'Child';
+
+ $this->dm->persist($parent);
+ $this->dm->flush();
+ $this->dm->clear();
+
+ $parent = $this->dm->find($this->type, '/functional/childtest');
+ $parent->child->name = 'new name';
+ $parent->child = null;
+ $this->dm->flush();
+ $this->dm->clear();
+
+ $parent = $this->dm->find($this->type, '/functional/childtest');
+ $this->assertNull($parent->child);
+ }
+
+ /* this fails as the newChild is not persisted */
+ public function testChildReplace()
+ {
+ $parent = new ChildTestObj();
+ $child = new ChildChildTestObj();
+ $parent->name = 'Parent';
+ $parent->id = '/functional/childtest';
+ $parent->child = $child;
+ $child->name = 'Child';
+
+ $this->dm->persist($parent);
+ $this->dm->flush();
+ $this->dm->clear();
+
+ $parent = $this->dm->find($this->type, '/functional/childtest');
+ $newChild = new ChildChildTestObj();
+ $newChild->name = 'new name';
+ $parent->child = $newChild;
+
+ $this->setExpectedException('Doctrine\ODM\PHPCR\PHPCRException');
+
+ $this->dm->flush();
+
+ }
+}
+
+
+/**
+ * @Document(alias="childTestObj")
+ */
+class ChildChildTestObj
+{
+ /** @Id */
+ public $id;
+ /** @Node */
+ public $node;
+ /** @String */
+ public $name;
+}
+/**
+ * @Document(alias="testObj")
+ */
+class ChildTestObj
+{
+ /** @Id */
+ public $id;
+ /** @Node */
+ public $node;
+ /** @String */
+ public $name;
+ /** @Child(name="test") */
+ public $child;
+}
View
96 tests/Doctrine/Tests/ODM/PHPCR/Functional/FileTest.php
@@ -0,0 +1,96 @@
+<?php
+
+namespace Doctrine\Tests\ODM\PHPCR\Functional;
+
+use Doctrine\ODM\PHPCR\Document\File;
+
+/**
+ * @group functional
+ */
+class FileTest extends \Doctrine\Tests\ODM\PHPCR\PHPCRFunctionalTestCase
+{
+ /**
+ * @var DocumentManager
+ */
+ private $dm;
+
+ private $type;
+ private $childType;
+
+ private $node;
+
+ public function setUp()
+ {
+ $this->type = 'Doctrine\Tests\ODM\PHPCR\Functional\FileTestObj';
+ $this->dm = $this->createDocumentManager();
+ $this->node = $this->resetFunctionalNode($this->dm);
+ }
+
+ public function testCreateFromFile()
+ {
+ $parent = new FileTestObj();
+ $parent->file = new File();
+ $parent->id = '/functional/filetest';
+ $parent->file->setFileContentFromFilesystem('Doctrine/Tests/ODM/PHPCR/Functional/_files/foo.txt');
+
+ $this->dm->persist($parent);
+ $this->dm->flush();
+ $this->dm->clear();
+
+ $this->assertTrue($this->node->getNode('filetest')->hasNode('file'));
+ $this->assertTrue($this->node->getNode('filetest')->getNode('file')->hasNode('jcr:content'));
+ $this->assertTrue($this->node->getNode('filetest')->getNode('file')->getNode('jcr:content')->hasProperty('jcr:data'));
+ }
+
+ public function testCreateFromString()
+ {
+ $parent = new FileTestObj();
+ $parent->file = new File();
+ $parent->id = '/functional/filetest';
+ $parent->file->setFileContent('Lorem ipsum dolor sit amet');
+
+ $this->dm->persist($parent);
+ $this->dm->flush();
+ $this->dm->clear();
+
+ $this->assertTrue($this->node->getNode('filetest')->hasNode('file'));
+ $this->assertTrue($this->node->getNode('filetest')->getNode('file')->hasNode('jcr:content'));
+ $this->assertTrue($this->node->getNode('filetest')->getNode('file')->getNode('jcr:content')->hasProperty('jcr:data'));
+ $binaryStream = $this->node->getNode('filetest')->getNode('file')->getNode('jcr:content')->getProperty('jcr:data')->getBinary();
+ $content = stream_get_contents($binaryStream);
+ $this->assertEquals('Lorem ipsum dolor sit amet', $content);
+ }
+
+ public function testCreatedDate()
+ {
+ $parent = new FileTestObj();
+ $parent->file = new File();
+ $parent->id = '/functional/filetest';
+ $parent->file->setFileContentFromFilesystem('Doctrine/Tests/ODM/PHPCR/Functional/_files/foo.txt');
+
+ $this->dm->persist($parent);
+ $this->dm->flush();
+ $this->dm->clear();
+
+ $file = $this->dm->find('Doctrine\ODM\PHPCR\Document\File', '/functional/filetest/file');
+
+ $this->assertNotNull($file);
+ $this->assertNotNull($file->getCreated());
+ }
+}
+
+
+/**
+ * @phpcr:Document(alias="testObj")
+ */
+class FileTestObj
+{
+ /** @phpcr:Id */
+ public $id;
+ /** @phpcr:Node */
+ public $node;
+ /** @phpcr:String */
+ public $name;
+ /** @phpcr:Child */
+ public $file;
+}
View
48 tests/Doctrine/Tests/ODM/PHPCR/Functional/FolderTest.php
@@ -0,0 +1,48 @@
+<?php
+
+namespace Doctrine\Tests\ODM\PHPCR\Functional;
+
+use Doctrine\ODM\PHPCR\Document\File;
+use Doctrine\ODM\PHPCR\Document\Folder;
+
+/**
+ * @group functional
+ */
+class FolderTest extends \Doctrine\Tests\ODM\PHPCR\PHPCRFunctionalTestCase
+{
+ /**
+ * @var DocumentManager
+ */
+ private $dm;
+
+ private $node;
+
+ public function setUp()
+ {
+ $this->dm = $this->createDocumentManager();
+ $this->node = $this->resetFunctionalNode($this->dm);
+ }
+
+ public function testCreate()
+ {
+ $folder = new Folder();
+ $folder->setId('/functional/folder');
+
+ $file = new File();
+ $file->setId('/functional/folder/file');
+ $file->setFileContent('Lorem ipsum dolor sit amet');
+
+ $this->dm->persist($folder);
+ $this->dm->persist($file);
+ $this->dm->flush();
+ $this->dm->clear();
+
+ $this->assertTrue($this->node->hasNode('folder'));
+ $this->assertTrue($this->node->getNode('folder')->hasNode('file'));
+ $this->assertTrue($this->node->getNode('folder')->getNode('file')->hasNode('jcr:content'));
+ $this->assertTrue($this->node->getNode('folder')->getNode('file')->getNode('jcr:content')->hasProperty('jcr:data'));
+ $binaryStream = $this->node->getNode('folder')->getNode('file')->getNode('jcr:content')->getProperty('jcr:data')->getBinary();
+ $content = stream_get_contents($binaryStream);
+ $this->assertEquals('Lorem ipsum dolor sit amet', $content);
+ }
+}
View
3  tests/Doctrine/Tests/ODM/PHPCR/Mapping/AbstractMappingDriverTest.php
@@ -29,8 +29,7 @@ public function testLoadMapping()
*/
public function testFieldMappings($class)
{
- $this->assertEquals(4, count($class->fieldMappings));
- $this->assertTrue(isset($class->fieldMappings['id']));
+ $this->assertEquals(3, count($class->fieldMappings));
$this->assertTrue(isset($class->fieldMappings['name']));
$this->assertTrue(isset($class->fieldMappings['username']));
$this->assertTrue(isset($class->fieldMappings['status']));
View
5 tests/Doctrine/Tests/ODM/PHPCR/PHPCRFunctionalTestCase.php
@@ -8,7 +8,10 @@ public function createDocumentManager()
{
$reader = new \Doctrine\Common\Annotations\AnnotationReader();
$reader->setDefaultAnnotationNamespace('Doctrine\ODM\PHPCR\Mapping\\');
- $paths = __DIR__ . "/../../Models";
+ $reader->setAnnotationNamespaceAlias('Doctrine\ODM\PHPCR\Mapping\\', 'phpcr');
+ $paths = array();
+ $paths[] = __DIR__ . "/../../Models";
+ $paths[] = __DIR__ . "/../../../../../lib/Doctrine/ODM/PHPCR/Document";
$metaDriver = new \Doctrine\ODM\PHPCR\Mapping\Driver\AnnotationDriver($reader, $paths);
$url = isset($GLOBALS['DOCTRINE_PHPCR_REPOSITORY']) ? $GLOBALS['DOCTRINE_PHPCR_REPOSITORY'] : 'http://127.0.0.1:8080/server/';
View
2  tests/Doctrine/Tests/ODM/PHPCR/Performance/InsertPerformanceTest.php
@@ -29,7 +29,7 @@ public function testInsertDocuments()
$user->name = "Benjamin";
$user->username = "beberlei";
$user->status = "active";
- $user->path = "/functional/node$i";
+ $user->id = "/functional/node$i";
$dm->persist($user);
}
Something went wrong with that request. Please try again.