NodeType Constraints

... how to restrict editor's choice

In a typical  project, you will create lots of custom NodeTypes. Many NodeTypes should only be used in a limited context and not elsewhere. Neos allows you to define NodeType constraints, which restrict the places where NodeTypes can be added. There are two ways to do this:

  • Regular NodeType constraints are defined per NodeType. They apply in any context the NodeType appears in.
  • Additionally, when a NodeType has auto-created child nodes, you can define additional constraints that only apply for these child nodes. This allows you to restrict NodeType usage depending on the context that the NodeTypes are placed in.
     

#Regular NodeType Constraints

Let’s assume that, inside the Chapter NodeType of the Neos Demo Site (which is a document node), one should only be able to create nested chapters, and not pages or shortcuts. Using NodeType constraints, this can be enforced:

Configuration/NodeTypes.Document.Chapter.yaml (partly)
'Neos.Demo:Document.Chapter':
  constraints:
    nodeTypes:
      'Neos.Neos:Document': false
      'Neos.Demo:Document.Chapter': true

In the above example, we disable all document NodeTypes using 'Neos.Neos:Document': false, and then enable the Neos.Demo:Document.Chapter NodeType as well as any NodeType that inherits from it. The reason why we use 'Neos.Neos:Document': false instead of '*': false here is that by default, only document NodeTypes are allowed as children of other document NodeTypes anyway (see further down for more information regarding the defaults).

You might now wonder why it is still possible to create content inside the chapter (because everything except Chapter is disabled with the above configuration): The reason is that NodeType constraints are only enforced for nodes which are not auto-created. Because Neos.Demo:Document.Chapter has an auto-created main ContentCollection, it is still possible to add content inside. In the following example, we see the NodeType definition which is shipped with the demo website:

Configuration/NodeTypes.Document.Chapter.yaml (partly)
'Neos.Demo:Document.Chapter':
  superTypes:
    'Neos.Neos:Document': true
  childNodes:
    'main':
      type: 'Neos.Neos:ContentCollection'

The main ContentCollection is still added, even though you cannot add any more because ContentCollections are not allowed according to the NodeType constraints.

The Neos.Neos:Document NodeType, by default, allows any other Neos.Neos:Document NodeType below it. This means that if you want to disable all document NodeTypes under your custom one, setting '*': false will have no effect on anything inheriting from Neos.Neos:Document - the more specific constraint 'Neos.Neos:Document': true will override it. You will need to set 'Neos.Neos:Document': false instead.

The default Neos.Neos:Content NodeType, on the other hand, only has the catch-all constraint. If you want to enable any child nodes, you can simply allow them.

yaml
'Neos.Neos:Content':
  constraints:
    nodeTypes:
      '*': false

#Auto-Created Child Node Constraints

Let’s assume that our chapter NodeTypes should only contain text within its main ContentCollection. This is possible using additional constraints for each auto-created child node. These constraints will only be applied for the configured auto-created child nodes - not for any others, even if they are of the same type.

yaml
'Neos.Demo:Document.Chapter':
  childNodes:
    'main':
      type: 'Neos.Neos:ContentCollection'
      constraints:
        nodeTypes:
          '*': false
          'Neos.NodeTypes:Text': true

Tip

We do not recommend anymore to use Neos.NodeTypes directly. Please build your own NodeTypes.

#In-Depth Behavior

The following logic applies for NodeType constraints:

  • Constraints are only enforced for child nodes which are not auto-created.
  • You can specify NodeTypes explicitly or use ‘*’ to allow/deny all NodeTypes.
  • Setting the value to true is an explicit allow
  • Setting the value to false is an explicit deny
  • The default is to always deny (in case ‘*’ is not specified).
  • More specific constraints override less specific constraints. Specificity is deduced from the inheritance hierarchy of the NodeTypes. This means that e.g. setting ‘*’: false will only apply if no more specific constraint has been set, such as ‘Neos.Neos:Document’: true.
  • NodeType constraints are inherited from parent NodeTypes. If your NodeType has listed Neos.Neos:Document as a superType, its constraints will apply for your NodeType as well.

The last rule is especially important, since most NodeTypes you define will have either Neos.NodeTypes:Page (which, in turn, inherits from Neos.Neos:Document) or Neos.Neos:Content as superTypes. You should know which constraints are defined per default in order to effectively override them. These are the current defaults for these two NodeTypes - this is taken from NodeTypes.yaml in the Neos.Neos package.

yaml
'Neos.Neos:Document':
  constraints:
    nodeTypes:
      '*': false
      'Neos.Neos:Document': true

The document NodeType, by default, allows any other document NodeType below it. This means that if you want to disable all document NodeTypes under your custom one, setting '*': false will have no effect on anything inheriting from Neos.Neos:Document - the more specific constraint 'Neos.Neos:Document': true will override it. You will need to set 'Neos.Neos:Document': false instead.

The default content NodeType, on the other hand, only has the catch-all constraint. If you want to enable any child nodes, you can simply allow them.

yaml
'Neos.Neos:Content':
  constraints:
    nodeTypes:
      '*': false