Preface
Let’s start off with a simple example. We have an Article node with ‘main’ content collection node. Now in most cases an editor would need to have at least one Text node in that collection. So after he creates an Article, he has to make a few more clicks to create the text node. Wouldn’t it be cool if we could prefill the freshly created node with some boilerplate child nodes?
Currently we have the childNodes
functionality, but it presuppose a strong coupling between parent and child nodes. It’s appropriate when child and parent nodes form an integral whole. But it can’t be used for the case of pre-filling nodes with boilerplate content, because it has two serious limitations: 1) the childNodes are fixed and permanent (you can’t move/remove) them; 2) you can’t define nested childNodes (e.g. put the Text node inside main ContentCollection node inside Article node).
Learning from past mistakes
Our previous attempt at solving this issue was great… But it was too imperative. This time we try to do the same thing in a completely different, more declarative way.
The Concept
Not to overburden the low-level concept of the node, we propose to introduce the concept of the node template. Node template represents a nested collection of nodes defined in a declarative way.
The templating would be used when a node is created from Neos UI (in REST service), populating the newly created node with child nodes.
Simple example:
'TYPO3.Neos.NodeTypes:Article':
template:
childNodes:
mainContentCollection: <-- this is just some key for extensibility
type: 'TYPO3.Neos:ContentCollection' <-- optional
name: 'main'
childNodes:
helloWorldTextNode:
type: 'TYPO3.Neos.NodeType:Text'
properties:
text: 'Hello world!'
When a node template is applied on node creation, the structure of the node tree will be adapted to the structure defined in the template; if a child node with the same name already exists, no second node is added.
If the node with the same name already exists but has a different type or different properties, it will be altered to match the template.
The child nodes array is named, but unnamed arrays are supported, too. It is good practice to use named arrays in packages that might be extended by integrators.
You can use EEL in all properties.
Loops and Conditions
The syntax for creating loops and conditions is inspired by Ansible. Instead of forEach
and if
we propose to call them withItems
and when
, because it inspires a more declarative mood. Ansible guys are cool and know how to make the most imperative things feel declarative
The Wizard
The data
context variable can be populated by a wizard that is opened on node creation and can be configured in the node type definition. Please see here for more details.
Complete example
'TYPO3.Neos.NodeTypes:Article':
template:
class: (optional, implements NodeTemplateInterface)
properties:
title: '${data.title}' ← sets the title property of a given node to data from the wizard
childNodes:
boilerplateChildDocument:
type: 'TYPO3.Neos.NodeTypes:ChildDocument'
properties:
title: 'Boilerplate child document node'
mainContentCollection:
name: 'main'
type: 'TYPO3.Neos:ContentCollection' ← optional, as the such node already exists
childNodes:
conditionedHelloWorldTextNode:
type: 'TYPO3.Neos.NodeType:Text'
properties:
text: 'Hello world!'
when: '${data.showHelloWorld}' ← condition example
multipleTextNodes:
type: 'TYPO3.Neos.NodeType:Text'
properties:
text: '${item + " item text"}' ← `item` points to current item in the `withItems` loop
withItems: ← loop over array, and create a text node for each
- 'First'
- 'Second'
createRowWithColumnsALaFoundation:
type: 'TYPO3.Neos.NodeTypes:Row'
childNodes:
columnNode:
type: 'TYPO3.Neos.NodeTypes:Column'
properties:
class: '${'columns small-' + 12 / data.numberOfColumns}'
repeat: '${data.numberOfColumns}' ← don’t know if this really makes sense
multipleImageNodesCreatedFromWizardData:
type: 'TYPO3.Neos.NodeTypes:Image'
properties:
asset: '${item.image}' ← sets an Image node’s asset to the data coming from the wizard
caption: '${item.caption}'
withItems: '${data.images}' ← loop over array of images
Implementation details
The technical basis for enriching nodes via node templates is the NodeTemplate
class that implements the NodeTemplateInterface
and that can handle the common configuration options (i.e. childNodes and properties). If you have more complex use cases, you can use an own class that implements the NodeTemplateInterface
and configure it via the “class” option (as you can do with node types).
We propose to put this functionality to TYPO3CR package, because this functionality is generic enough to be used outside of Neos. However this functionality will not be triggered by default in CR itself. The CR would just provide the service, which would then be called from Neos’ REST controller, but can also be triggered from user controllers or commands.
~ Thomas and Dmitri