Creating custom NodeTypes
This post is the fifth in a serie about PHPCR with Jackalope. In the first post we've setup our repository, in the second we talked about reading and writing from and to our repository. The third post was about the QueryObjectModel and in the previous post we talked about NodeTypes and Mixins.
In this post we'll be creating our own NodeType.
Defining the NodeType
As we saw in the previous post, NodeTypes have certain properties that define how a NodeType behaves and property definitions that define what values are stored in the NodeType. To create our own NodeType, we need to tell our repository how our NodeType should behave.
Our NodeType should have a name which shouldn't clash with other NodeTypes. To be sure of that, we need to register a new namespace with our repository and use that namespace to name our NodeType.
$session->getWorkspace()->getNamespaceRegistry()
->registerNamespace('acme', 'http://acme.example.com/phpcr/1.0');
To define the actual NodeType we need to use the NodeTypeManager
, that manager takes care of registering the NodeType
with our repository. We need to pass a NodeTypeDefinition
to NodeTypeManager::registerNodeType
. We can build it from
an array, from XML or create a copy from another NodeTypeDefinition instance.
In this case I went for the XML solution which has the benefit that you can easily distribute the NodeType so others can use it without the need for PHP.
<!-- customNodeTypes.xml -->
<?xml version="1.0" encoding="utf-8"?>
<nodeTypes>
<nodeType
name="acme:product" isMixin="false" isAbstract="false"
isQueryable="true" hasOrderableChildNodes="true">
<supertypes>
<supertype>nt:base</supertype>
<supertype>mix:title</supertype>
<supertype>mix:referenceable</supertype>
</supertypes>
<propertyDefinition
name="acme:rrpPrice"
requiredType="decimal"
declaringNodeType="acme:product"
autoCreated="true"
fullTextSearchable="false"
mandatory="true"
multiple="false"
onParentVersion="COPY"
protected="false"
queryOrderable="true">
<valueConstraints />
</propertyDefinition>
<propertyDefinition
name="acme:media"
requiredType="Reference"
declaringNodeType="acme:product"
autoCreated="false"
fullTextSearchable="false"
mandatory="false"
multiple="true"
onParentVersion="COPY"
protected="false"
queryOrderable="false">
<valueConstraints>
<valueConstraint>nt:resource</valueConstraint>
</valueConstraints>
</propertyDefinition>
</nodeType>
</nodeTypes>
The definition above gives us a acme:product NodeType with 2 properties. Beside that, we also defined that it has 3
supertypes: nt:base
, mix:title
and mix:reference
. For the acme:media property, we also added a valueConstraint.
Registering the NodeType
To actually register the nodeType, we only need a few lines of code as seen in the CreateNodeTypeCommand
$nodeTypesDocument = new \DOMDocument();
$nodeTypesDocument->load(__DIR__ . '/../Resources/data/customNodeTypes.xml');
$xpath = new \DOMXPath($nodeTypesDocument);
foreach ($xpath->query('//nodeType') as $nodeTypeElement) {
$nodeType = new NodeType(
new Factory(),
$session->getWorkspace()->getNodeTypeManager(),
$nodeTypeElement
);
$session->getWorkspace()
->getNodeTypeManager()
->registerNodeType($nodeType, true);
}
$session->save();
Sidenote: above code won't work correctly untill PR 203 has been merged.
Using the newly created NodeType
To use the new NodeType, we need to create a Node with the new NodeType as primaryType.
$rootNode->addNode('customNodeTypeNode', 'acme:product');
We new now try to save the product, we get an error stating that the property acme:rrpPrice doesn't have a default value.
If we fill the price the node saves just fine. After that we can also add a property jcr:title, which is defined by our
supertype mix:title. But when we also try to add a property foo
with value bar
, we get an exception that our
NodeTypeDefinition doesn't allow to set a property foo
.
By creating custom NodeTypes, you can create some structure in your data, while still having the flexibility from a schemaless storage.
The examples in this blogpost can be found on Github.