class NodeMoveIntegrityCheckService (View source)


This is an internal service to prevent disconnected nodes when moving.

It is called from {\Neos\ContentRepository\Domain\Model\Node::setPathInternalForAggregate()}, and throws an error in case disconnected nodes might appear due to a move.

How can disconnected nodes occur?

Background: When moving a node, it is moved across ALL dimensions.

(This issue is described at )

Let's say we have the following Node structure in the beginning:

|-- site (de, en) . |-- cr (de, en) . | |-- subpage (de, en) . |-- other (de)

Now, the user moves /site/cr/subpage underneath /site/other/ in the user workspace.

Until October 2021, this lead to the following situation / bug:

|-- site (de, en) . |-- cr (de, en) . |-- other (de) . | |-- subpage (de, en) <----- this page is DISCONNECTED in EN (as the parent page only exists in DE).

This service detects these kinds of problems and outputs a long explanation, suitable for displaying to an end-user.

How to fix the Issue: Preventing the move completely

Background: This algorithm is called just ONCE for the root of the moved node tree.

Basic Idea: For all dimensions where a node exists, do:

  • if the node variant had a parent before the move, and
  • does not have a parent after the move,
  • => we throw an error.

Due to Dimension Fallbacks, we cannot ask the NodeData instances directly for their parents, but we instead need to loop over the Dimension Presets to handle fallbacks correctly.

Alternative Solution Idea which won't work: Don't move across dimensions in all cases

We also explored to relax our idea that document nodes across dimensions must ALWAYS move in sync.

In the above example, we could also only move a certain variant (where the parent exists at the target). However, this leads to the following classes of problems:

1) When Dimension FALLBACKS are used, we might need to COPY a node variant (because it is needed BOTH at the original location e.g. in DE AND the target location in e.g. AT (if AT falls back to DE); but due to the fallback mechanism only one NodeData object exists).

2) The problem above (1) could not only occur for the moved node, but also for arbitrary descendants. This would mean that a "move" would need to copy NodeData objects around many levels deep - which is completely non-understandable to users.

3) For an editor, the content trees can fully diverge - so that's also difficult to communicate.


protected ContentDimensionCombinator $contentDimensionCombinator
protected ContentDimensionPresetSourceInterface $contentDimensionPresetSource
protected ContextFactoryInterface $contextFactory


ensureIntegrityForDocumentNodeMove(NodeInterface $nodeToMove, string $destinationPath)

No description

checkIntegrityForDocumentNodeMove(NodeInterface $nodeToMove, string $destinationParentPath)

No description


void ensureIntegrityForDocumentNodeMove(NodeInterface $nodeToMove, string $destinationPath)

No description


NodeInterface $nodeToMove
string $destinationPath

Return Value


protected NodeMoveIntegrityCheckResult checkIntegrityForDocumentNodeMove(NodeInterface $nodeToMove, string $destinationParentPath)

No description


NodeInterface $nodeToMove
string $destinationParentPath

Return Value