From b4fe67e169446987a1a9a09bc62c6a13a1b51aca Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Tue, 20 Feb 2024 17:45:12 +0100 Subject: [PATCH 01/12] !!! TASK: Refine NodeTypeManager API to use `NodeTypeName` as parameters Followup to #4520 --- .../Feature/Common/ConstraintChecks.php | 6 ++--- .../Dto/NodeAggregateIdsByNodePaths.php | 2 +- .../Feature/NodeCreation/NodeCreation.php | 11 ++++---- .../Feature/NodeTypeChange/NodeTypeChange.php | 11 ++++---- .../RootNodeCreation/RootNodeHandling.php | 11 ++++---- .../Classes/NodeType/NodeTypeManager.php | 26 ++++++++++--------- .../DisallowedChildNodeAdjustment.php | 12 ++++----- .../Adjustment/TetheredNodeAdjustments.php | 2 +- .../Classes/Service/NodeTypeSchemaBuilder.php | 8 +++--- 9 files changed, 44 insertions(+), 45 deletions(-) diff --git a/Neos.ContentRepository.Core/Classes/Feature/Common/ConstraintChecks.php b/Neos.ContentRepository.Core/Classes/Feature/Common/ConstraintChecks.php index 5674559e866..c7a064f4cc6 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/Common/ConstraintChecks.php +++ b/Neos.ContentRepository.Core/Classes/Feature/Common/ConstraintChecks.php @@ -177,7 +177,7 @@ protected function requireRootNodeTypeToBeUnoccupied( protected function requireTetheredDescendantNodeTypesToExist(NodeType $nodeType): void { // this getter throws if any of the child nodeTypes doesnt exist! - $tetheredNodeTypes = $this->getNodeTypeManager()->getTetheredNodesConfigurationForNodeType($nodeType); + $tetheredNodeTypes = $this->getNodeTypeManager()->getTetheredNodesConfigurationForNodeType($nodeType->name); foreach ($tetheredNodeTypes as $tetheredNodeType) { $this->requireTetheredDescendantNodeTypesToExist($tetheredNodeType); } @@ -189,7 +189,7 @@ protected function requireTetheredDescendantNodeTypesToExist(NodeType $nodeType) */ protected function requireTetheredDescendantNodeTypesToNotBeOfTypeRoot(NodeType $nodeType): void { - foreach ($this->getNodeTypeManager()->getTetheredNodesConfigurationForNodeType($nodeType) as $tetheredChildNodeType) { + foreach ($this->getNodeTypeManager()->getTetheredNodesConfigurationForNodeType($nodeType->name) as $tetheredChildNodeType) { if ($tetheredChildNodeType->isOfType(NodeTypeName::ROOT_NODE_TYPE_NAME)) { throw new NodeTypeIsOfTypeRoot( 'Node type "' . $nodeType->name->value . '" for tethered descendant is of type root.', @@ -370,7 +370,7 @@ protected function areNodeTypeConstraintsImposedByGrandparentValid( ): bool { return !($parentNodeName && $grandParentsNodeType->hasTetheredNode($parentNodeName) - && !$this->getNodeTypeManager()->isNodeTypeAllowedAsChildToTetheredNode($grandParentsNodeType, $parentNodeName, $nodeType)); + && !$this->getNodeTypeManager()->isNodeTypeAllowedAsChildToTetheredNode($grandParentsNodeType->name, $parentNodeName, $nodeType->name)); } /** diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeCreation/Dto/NodeAggregateIdsByNodePaths.php b/Neos.ContentRepository.Core/Classes/Feature/NodeCreation/Dto/NodeAggregateIdsByNodePaths.php index 3d289ec0385..d5eda29f1d9 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/NodeCreation/Dto/NodeAggregateIdsByNodePaths.php +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeCreation/Dto/NodeAggregateIdsByNodePaths.php @@ -128,7 +128,7 @@ private static function createNodeAggregateIdsForNodeType( ?string $pathPrefix = null ): array { $nodeAggregateIds = []; - foreach ($nodeTypeManager->getTetheredNodesConfigurationForNodeType($nodeTypeManager->requireNodeType($nodeTypeName)) as $nodeName => $childNodeType) { + foreach ($nodeTypeManager->getTetheredNodesConfigurationForNodeType($nodeTypeName) as $nodeName => $childNodeType) { $path = $pathPrefix ? $pathPrefix . '/' . $nodeName : $nodeName; $nodeAggregateIds[$path] = NodeAggregateId::create(); $nodeAggregateIds = array_merge( diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeCreation/NodeCreation.php b/Neos.ContentRepository.Core/Classes/Feature/NodeCreation/NodeCreation.php index 1c38334ff2d..6e8666fa09e 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/NodeCreation/NodeCreation.php +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeCreation/NodeCreation.php @@ -209,7 +209,7 @@ private function handleCreateNodeAggregateWithNodeAndSerializedProperties( array_push($events, ...iterator_to_array($this->handleTetheredChildNodes( $command, $contentGraph, - $nodeType, + $command->nodeTypeName, $coveredDimensionSpacePoints, $command->nodeAggregateId, $descendantNodeAggregateIds, @@ -257,16 +257,15 @@ private function createRegularWithNode( private function handleTetheredChildNodes( CreateNodeAggregateWithNodeAndSerializedProperties $command, ContentGraphInterface $contentGraph, - NodeType $nodeType, + NodeTypeName $nodeTypeName, DimensionSpacePointSet $coveredDimensionSpacePoints, NodeAggregateId $parentNodeAggregateId, NodeAggregateIdsByNodePaths $nodeAggregateIds, ?NodePath $nodePath ): Events { $events = []; - foreach ($this->getNodeTypeManager()->getTetheredNodesConfigurationForNodeType($nodeType) as $rawNodeName => $childNodeType) { - assert($childNodeType instanceof NodeType); - $nodeName = NodeName::fromString($rawNodeName); + foreach ($this->getNodeTypeManager()->getTetheredNodesConfigurationForNodeType($nodeTypeName) as $nodeNameString => $childNodeType) { + $nodeName = NodeName::fromString($nodeNameString); $childNodePath = $nodePath ? $nodePath->appendPathSegment($nodeName) : NodePath::fromString($nodeName->value); @@ -289,7 +288,7 @@ private function handleTetheredChildNodes( array_push($events, ...iterator_to_array($this->handleTetheredChildNodes( $command, $contentGraph, - $childNodeType, + $childNodeType->name, $coveredDimensionSpacePoints, $childNodeAggregateId, $nodeAggregateIds, diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeTypeChange/NodeTypeChange.php b/Neos.ContentRepository.Core/Classes/Feature/NodeTypeChange/NodeTypeChange.php index 39219ddfd1b..16219eb7160 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/NodeTypeChange/NodeTypeChange.php +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeTypeChange/NodeTypeChange.php @@ -29,6 +29,7 @@ use Neos\ContentRepository\Core\NodeType\NodeType; use Neos\ContentRepository\Core\NodeType\NodeTypeManager; use Neos\ContentRepository\Core\Projection\ContentGraph\ContentGraphInterface; +use Neos\ContentRepository\Core\NodeType\NodeTypeName; use Neos\ContentRepository\Core\Projection\ContentGraph\Node; use Neos\ContentRepository\Core\Projection\ContentGraph\NodeAggregate; use Neos\ContentRepository\Core\Projection\ContentGraph\NodePath; @@ -148,7 +149,7 @@ private function handleChangeNodeAggregateType( * Preparation - make the command fully deterministic in case of rebase **************/ $descendantNodeAggregateIds = $command->tetheredDescendantNodeAggregateIds->completeForNodeOfType( - $newNodeType->name, + $command->newNodeTypeName, $this->nodeTypeManager ); // Write the auto-created descendant node aggregate ids back to the command; @@ -176,12 +177,12 @@ private function handleChangeNodeAggregateType( array_push($events, ...iterator_to_array($this->deleteObsoleteTetheredNodesWhenChangingNodeType( $contentGraph, $nodeAggregate, - $newNodeType + $command->newNodeTypeName ))); } // new tethered child nodes - $expectedTetheredNodes = $this->getNodeTypeManager()->getTetheredNodesConfigurationForNodeType($newNodeType); + $expectedTetheredNodes = $this->getNodeTypeManager()->getTetheredNodesConfigurationForNodeType($command->newNodeTypeName); foreach ($nodeAggregate->getNodes() as $node) { assert($node instanceof Node); foreach ($expectedTetheredNodes as $serializedTetheredNodeName => $expectedTetheredNodeType) { @@ -347,9 +348,9 @@ private function deleteDisallowedNodesWhenChangingNodeType( private function deleteObsoleteTetheredNodesWhenChangingNodeType( ContentGraphInterface $contentGraph, NodeAggregate $nodeAggregate, - NodeType $newNodeType + NodeTypeName $newNodeTypeName ): Events { - $expectedTetheredNodes = $this->getNodeTypeManager()->getTetheredNodesConfigurationForNodeType($newNodeType); + $expectedTetheredNodes = $this->getNodeTypeManager()->getTetheredNodesConfigurationForNodeType($newNodeTypeName); $events = []; // find disallowed tethered nodes diff --git a/Neos.ContentRepository.Core/Classes/Feature/RootNodeCreation/RootNodeHandling.php b/Neos.ContentRepository.Core/Classes/Feature/RootNodeCreation/RootNodeHandling.php index 8bd2e3cb142..ab6f0a1b9a5 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/RootNodeCreation/RootNodeHandling.php +++ b/Neos.ContentRepository.Core/Classes/Feature/RootNodeCreation/RootNodeHandling.php @@ -105,7 +105,7 @@ private function handleCreateRootNodeAggregateWithNode( foreach ($this->getInterDimensionalVariationGraph()->getRootGeneralizations() as $rootGeneralization) { array_push($events, ...iterator_to_array($this->handleTetheredRootChildNodes( $contentGraph->getContentStreamId(), - $nodeType, + $command->nodeTypeName, OriginDimensionSpacePoint::fromDimensionSpacePoint($rootGeneralization), $this->getInterDimensionalVariationGraph()->getSpecializationSet($rootGeneralization, true), $command->nodeAggregateId, @@ -184,7 +184,7 @@ private function handleUpdateRootNodeAggregateDimensions( */ private function handleTetheredRootChildNodes( ContentStreamId $contentStreamId, - NodeType $nodeType, + NodeTypeName $nodeTypeName, OriginDimensionSpacePoint $originDimensionSpacePoint, DimensionSpacePointSet $coveredDimensionSpacePoints, NodeAggregateId $parentNodeAggregateId, @@ -192,9 +192,8 @@ private function handleTetheredRootChildNodes( ?NodePath $nodePath ): Events { $events = []; - foreach ($this->getNodeTypeManager()->getTetheredNodesConfigurationForNodeType($nodeType) as $rawNodeName => $childNodeType) { - assert($childNodeType instanceof NodeType); - $nodeName = NodeName::fromString($rawNodeName); + foreach ($this->getNodeTypeManager()->getTetheredNodesConfigurationForNodeType($nodeTypeName) as $nodeNameString => $childNodeType) { + $nodeName = NodeName::fromString($nodeNameString); $childNodePath = $nodePath ? $nodePath->appendPathSegment($nodeName) : NodePath::fromString($nodeName->value); @@ -215,7 +214,7 @@ private function handleTetheredRootChildNodes( array_push($events, ...iterator_to_array($this->handleTetheredRootChildNodes( $contentStreamId, - $childNodeType, + $childNodeType->name, $originDimensionSpacePoint, $coveredDimensionSpacePoints, $childNodeAggregateId, diff --git a/Neos.ContentRepository.Core/Classes/NodeType/NodeTypeManager.php b/Neos.ContentRepository.Core/Classes/NodeType/NodeTypeManager.php index 7ade13450ab..0dcaf64fc58 100644 --- a/Neos.ContentRepository.Core/Classes/NodeType/NodeTypeManager.php +++ b/Neos.ContentRepository.Core/Classes/NodeType/NodeTypeManager.php @@ -191,14 +191,14 @@ public function overrideNodeTypes(array $completeNodeTypeConfiguration): void } /** - * @param NodeType $nodeType + * @param NodeTypeName $nodeTypeName * @param NodeName $tetheredNodeName * @return NodeType * @throws TetheredNodeNotConfigured if the requested tethered node is not configured. Check via {@see NodeType::hasTetheredNode()}. */ - public function getTypeOfTetheredNode(NodeType $nodeType, NodeName $tetheredNodeName): NodeType + public function getTypeOfTetheredNode(NodeTypeName $nodeTypeName, NodeName $tetheredNodeName): NodeType { - $nameOfTetheredNode = $nodeType->getNodeTypeNameOfTetheredNode($tetheredNodeName); + $nameOfTetheredNode = $this->requireNodeType($nodeTypeName)->getNodeTypeNameOfTetheredNode($tetheredNodeName); return $this->requireNodeType($nameOfTetheredNode); } @@ -207,9 +207,9 @@ public function getTypeOfTetheredNode(NodeType $nodeType, NodeName $tetheredNode * * @return array the key of this array is the name of the child, and the value its NodeType. */ - public function getTetheredNodesConfigurationForNodeType(NodeType $nodeType): array + public function getTetheredNodesConfigurationForNodeType(NodeTypeName $nodeTypeName): array { - $childNodeConfiguration = $nodeType->getConfiguration('childNodes'); + $childNodeConfiguration = $this->requireNodeType($nodeTypeName)->getConfiguration('childNodes'); $autoCreatedChildNodes = []; foreach ($childNodeConfiguration ?? [] as $childNodeName => $configurationForChildNode) { if (isset($configurationForChildNode['type'])) { @@ -225,21 +225,21 @@ public function getTetheredNodesConfigurationForNodeType(NodeType $nodeType): ar * * Only allowed to be called if $tetheredNodeName is actually tethered. * - * @param NodeType $parentNodeType The NodeType to check constraints based on. + * @param NodeTypeName $parentNodeTypeName The NodeType to check constraints based on. * @param NodeName $tetheredNodeName The name of a configured tethered node of this NodeType - * @param NodeType $nodeType The NodeType to check constraints for. + * @param NodeTypeName $nodeTypeName The NodeType to check constraints for. * @return bool true if the $nodeType is allowed as grandchild node, false otherwise. * @throws \InvalidArgumentException if the requested tethered node is not configured in the parent NodeType. Check via {@see NodeType::hasTetheredNode()}. */ - public function isNodeTypeAllowedAsChildToTetheredNode(NodeType $parentNodeType, NodeName $tetheredNodeName, NodeType $nodeType): bool + public function isNodeTypeAllowedAsChildToTetheredNode(NodeTypeName $parentNodeTypeName, NodeName $tetheredNodeName, NodeTypeName $nodeTypeName): bool { try { - $typeOfTetheredNode = $this->getTypeOfTetheredNode($parentNodeType, $tetheredNodeName); + $typeOfTetheredNode = $this->getTypeOfTetheredNode($parentNodeTypeName, $tetheredNodeName); } catch (TetheredNodeNotConfigured $exception) { throw new \InvalidArgumentException( sprintf( 'Cannot determine if grandchild is allowed in %s. Because the given child node name "%s" is not auto-created.', - $parentNodeType->name->value, + $parentNodeTypeName->value, $tetheredNodeName->value ), 1403858395, @@ -252,7 +252,7 @@ public function isNodeTypeAllowedAsChildToTetheredNode(NodeType $parentNodeType, // Constraints configured at the childNode configuration of the parent. try { - $childNodeConstraintConfiguration = $parentNodeType->getConfiguration('childNodes.' . $tetheredNodeName->value . '.constraints.nodeTypes') ?? []; + $childNodeConstraintConfiguration = $this->requireNodeType($parentNodeTypeName)->getConfiguration('childNodes.' . $tetheredNodeName->value . '.constraints.nodeTypes') ?? []; } catch (PropertyNotAccessibleException $exception) { // We ignore this because the configuration might just not have any constraints, if the childNode was not configured the exception above would have been thrown. $childNodeConstraintConfiguration = []; @@ -260,7 +260,9 @@ public function isNodeTypeAllowedAsChildToTetheredNode(NodeType $parentNodeType, $constraints = Arrays::arrayMergeRecursiveOverrule($constraints, $childNodeConstraintConfiguration); - return ConstraintCheck::create($constraints)->isNodeTypeAllowed($nodeType); + return ConstraintCheck::create($constraints)->isNodeTypeAllowed( + $this->requireNodeType($nodeTypeName) + ); } /** diff --git a/Neos.ContentRepository.StructureAdjustment/src/Adjustment/DisallowedChildNodeAdjustment.php b/Neos.ContentRepository.StructureAdjustment/src/Adjustment/DisallowedChildNodeAdjustment.php index 5f602315ddd..e3febf174f8 100644 --- a/Neos.ContentRepository.StructureAdjustment/src/Adjustment/DisallowedChildNodeAdjustment.php +++ b/Neos.ContentRepository.StructureAdjustment/src/Adjustment/DisallowedChildNodeAdjustment.php @@ -71,19 +71,17 @@ public function findAdjustmentsForNodeType(NodeTypeName $nodeTypeName): \Generat } $allowedByGrandparent = false; - $grandparentNodeType = null; if ( $parentNode !== null && $grandparentNode !== null && $parentNode->classification->isTethered() && !is_null($parentNode->nodeName) ) { - $grandparentNodeType = $this->nodeTypeManager->hasNodeType($grandparentNode->nodeTypeName) ? $this->nodeTypeManager->getNodeType($grandparentNode->nodeTypeName) : null; - if ($grandparentNodeType !== null) { + if ($this->nodeTypeManager->hasNodeType($grandparentNode->nodeTypeName)) { $allowedByGrandparent = $this->nodeTypeManager->isNodeTypeAllowedAsChildToTetheredNode( - $grandparentNodeType, + $grandparentNode->nodeTypeName, $parentNode->nodeName, - $nodeType + $nodeAggregate->nodeTypeName ); } } @@ -100,9 +98,9 @@ public function findAdjustmentsForNodeType(NodeTypeName $nodeTypeName): \Generat and the grandparent node type "%s" is not allowing grandchildren of type "%s". Thus, the node is invalid at this location and should be removed. ', - $parentNodeType !== null ? $parentNodeType->name->value : '', + $parentNode?->nodeTypeName->value ?? '', $node->nodeTypeName->value, - $grandparentNodeType !== null ? $grandparentNodeType->name->value : '', + $grandparentNode?->nodeTypeName->value ?? '', $node->nodeTypeName->value, ); diff --git a/Neos.ContentRepository.StructureAdjustment/src/Adjustment/TetheredNodeAdjustments.php b/Neos.ContentRepository.StructureAdjustment/src/Adjustment/TetheredNodeAdjustments.php index 73195ff0991..f58e4bb02f2 100644 --- a/Neos.ContentRepository.StructureAdjustment/src/Adjustment/TetheredNodeAdjustments.php +++ b/Neos.ContentRepository.StructureAdjustment/src/Adjustment/TetheredNodeAdjustments.php @@ -51,7 +51,7 @@ public function findAdjustmentsForNodeType(NodeTypeName $nodeTypeName): \Generat // In case we cannot find the expected tethered nodes, this fix cannot do anything. return; } - $expectedTetheredNodes = $this->nodeTypeManager->getTetheredNodesConfigurationForNodeType($nodeType); + $expectedTetheredNodes = $this->nodeTypeManager->getTetheredNodesConfigurationForNodeType($nodeTypeName); foreach ($this->projectedNodeIterator->nodeAggregatesOfType($nodeTypeName) as $nodeAggregate) { // find missing tethered nodes diff --git a/Neos.Neos/Classes/Service/NodeTypeSchemaBuilder.php b/Neos.Neos/Classes/Service/NodeTypeSchemaBuilder.php index 9e1d623a497..4f6d755a356 100644 --- a/Neos.Neos/Classes/Service/NodeTypeSchemaBuilder.php +++ b/Neos.Neos/Classes/Service/NodeTypeSchemaBuilder.php @@ -15,6 +15,7 @@ namespace Neos\Neos\Service; use Neos\ContentRepository\Core\NodeType\NodeType; +use Neos\ContentRepository\Core\NodeType\NodeTypeName; use Neos\ContentRepository\Core\NodeType\NodeTypeManager; use Neos\ContentRepository\Core\SharedModel\Node\NodeName; @@ -94,7 +95,6 @@ protected function generateConstraints() { $constraints = []; $nodeTypes = $this->nodeTypeManager->getNodeTypes(true); - /** @var NodeType $nodeType */ foreach ($nodeTypes as $nodeTypeName => $nodeType) { if ($nodeType->isAbstract()) { continue; @@ -109,9 +109,9 @@ protected function generateConstraints() } } - foreach ($this->nodeTypeManager->getTetheredNodesConfigurationForNodeType($nodeType) as $key => $_x) { - foreach ($nodeTypes as $innerNodeTypeName => $innerNodeType) { - if ($this->nodeTypeManager->isNodeTypeAllowedAsChildToTetheredNode($nodeType, NodeName::fromString($key), $innerNodeType)) { + foreach ($this->nodeTypeManager->getTetheredNodesConfigurationForNodeType($nodeType->name) as $key => $_x) { + foreach ($nodeTypes as $innerNodeTypeName => $_y) { + if ($this->nodeTypeManager->isNodeTypeAllowedAsChildToTetheredNode($nodeType->name, NodeName::fromString($key), NodeTypeName::fromString($innerNodeTypeName))) { $constraints[$nodeTypeName]['childNodes'][$key]['nodeTypes'][$innerNodeTypeName] = true; } } From f8bf8db47304ac41d7a992d02b79b6e4f3476107 Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Wed, 3 Apr 2024 21:28:18 +0200 Subject: [PATCH 02/12] TASK: Make `requireNodeType` private --- .../Classes/Feature/Common/TetheredNodeInternals.php | 4 ++-- .../Classes/NodeType/NodeTypeManager.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Neos.ContentRepository.Core/Classes/Feature/Common/TetheredNodeInternals.php b/Neos.ContentRepository.Core/Classes/Feature/Common/TetheredNodeInternals.php index 0a8f10ce699..c99412cd353 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/Common/TetheredNodeInternals.php +++ b/Neos.ContentRepository.Core/Classes/Feature/Common/TetheredNodeInternals.php @@ -69,8 +69,8 @@ protected function createEventsForMissingTetheredNode( if ($childNodeAggregate === null) { // there is no tethered child node aggregate already; let's create it! - $nodeType = $this->nodeTypeManager->requireNodeType($parentNodeAggregate->nodeTypeName); - if ($nodeType->isOfType(NodeTypeName::ROOT_NODE_TYPE_NAME)) { + $nodeType = $this->nodeTypeManager->getNodeType($parentNodeAggregate->nodeTypeName); + if ($nodeType?->isOfType(NodeTypeName::ROOT_NODE_TYPE_NAME)) { $events = []; $tetheredNodeAggregateId = $tetheredNodeAggregateId ?: NodeAggregateId::create(); // we create in one origin DSP and vary in the others diff --git a/Neos.ContentRepository.Core/Classes/NodeType/NodeTypeManager.php b/Neos.ContentRepository.Core/Classes/NodeType/NodeTypeManager.php index 0dcaf64fc58..7cd5475ebd1 100644 --- a/Neos.ContentRepository.Core/Classes/NodeType/NodeTypeManager.php +++ b/Neos.ContentRepository.Core/Classes/NodeType/NodeTypeManager.php @@ -382,9 +382,9 @@ private function evaluateSuperTypeConfiguration( } /** - * @internal helper to throw if the NodeType doesn't exit + * Helper to throw if the NodeType doesn't exit */ - public function requireNodeType(string|NodeTypeName $nodeTypeName): NodeType + private function requireNodeType(string|NodeTypeName $nodeTypeName): NodeType { return $this->getNodeType($nodeTypeName) ?? throw new NodeTypeNotFoundException( sprintf( From ba5f7665d4cb7c6a8f5a1a5f3810912feb509333 Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Wed, 1 May 2024 14:11:08 +0200 Subject: [PATCH 03/12] TASK: Fix unit test to adjusted `getTetheredNodesConfigurationForNodeType` api --- .../Tests/Unit/NodeType/NodeTypeManagerTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Neos.ContentRepository.Core/Tests/Unit/NodeType/NodeTypeManagerTest.php b/Neos.ContentRepository.Core/Tests/Unit/NodeType/NodeTypeManagerTest.php index db140498d82..017274ad619 100644 --- a/Neos.ContentRepository.Core/Tests/Unit/NodeType/NodeTypeManagerTest.php +++ b/Neos.ContentRepository.Core/Tests/Unit/NodeType/NodeTypeManagerTest.php @@ -433,8 +433,8 @@ public function anInheritedNodeTypePropertyCanBeOverruledWithEmptyArray(): void */ public function getAutoCreatedChildNodesReturnsLowercaseNames() { - $parentNodeType = $this->nodeTypeManager->getNodeType(NodeTypeName::fromString('Neos.ContentRepository.Testing:Page2')); - $autoCreatedChildNodes = $this->nodeTypeManager->getTetheredNodesConfigurationForNodeType($parentNodeType); + $parentNodeTypeName = NodeTypeName::fromString('Neos.ContentRepository.Testing:Page2'); + $autoCreatedChildNodes = $this->nodeTypeManager->getTetheredNodesConfigurationForNodeType($parentNodeTypeName); // This is configured as "nodeName" above, but should be normalized to "nodename" self::assertArrayHasKey('nodename', $autoCreatedChildNodes); } From ed63a5f7da55c5aa6a1bbddf97979039a4f6c9a3 Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Sun, 12 May 2024 20:10:00 +0200 Subject: [PATCH 04/12] TASK: Make `NodeType::getNodeTypeNameOfTetheredNode` return `null` instead of throwing --- .../Classes/NodeType/NodeType.php | 16 +++------------- .../Classes/NodeType/NodeTypeManager.php | 6 +++++- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php b/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php index 913b286188d..11409b0ada1 100644 --- a/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php +++ b/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php @@ -493,20 +493,10 @@ public function getDefaultValuesForProperties(): array public function hasTetheredNode(NodeName $nodeName): bool { $this->initialize(); - foreach ($this->fullConfiguration['childNodes'] ?? [] as $rawChildNodeName => $configurationForChildNode) { - if (isset($configurationForChildNode['type'])) { - if (NodeName::transliterateFromString($rawChildNodeName)->equals($nodeName)) { - return true; - } - } - } - return false; + return (bool)$this->getNodeTypeNameOfTetheredNode($nodeName); } - /** - * @throws TetheredNodeNotConfigured if the requested tethred node is not configured. Check via {@see NodeType::hasTetheredNode()}. - */ - public function getNodeTypeNameOfTetheredNode(NodeName $nodeName): NodeTypeName + public function getNodeTypeNameOfTetheredNode(NodeName $nodeName): ?NodeTypeName { $this->initialize(); foreach ($this->fullConfiguration['childNodes'] ?? [] as $rawChildNodeName => $configurationForChildNode) { @@ -516,7 +506,7 @@ public function getNodeTypeNameOfTetheredNode(NodeName $nodeName): NodeTypeName } } } - throw new TetheredNodeNotConfigured(sprintf('The child node "%s" is not configured for node type "%s"', $nodeName->value, $this->name->value), 1694786811); + return null; } /** diff --git a/Neos.ContentRepository.Core/Classes/NodeType/NodeTypeManager.php b/Neos.ContentRepository.Core/Classes/NodeType/NodeTypeManager.php index 7cd5475ebd1..4d61d6a7312 100644 --- a/Neos.ContentRepository.Core/Classes/NodeType/NodeTypeManager.php +++ b/Neos.ContentRepository.Core/Classes/NodeType/NodeTypeManager.php @@ -198,7 +198,11 @@ public function overrideNodeTypes(array $completeNodeTypeConfiguration): void */ public function getTypeOfTetheredNode(NodeTypeName $nodeTypeName, NodeName $tetheredNodeName): NodeType { - $nameOfTetheredNode = $this->requireNodeType($nodeTypeName)->getNodeTypeNameOfTetheredNode($tetheredNodeName); + $nodeType = $this->requireNodeType($nodeTypeName); + $nameOfTetheredNode = $nodeType->getNodeTypeNameOfTetheredNode($tetheredNodeName); + if ($nameOfTetheredNode === null) { + throw new TetheredNodeNotConfigured(sprintf('The child node "%s" is not configured for node type "%s"', $tetheredNodeName->value, $nodeTypeName->value), 1694786811); + } return $this->requireNodeType($nameOfTetheredNode); } From 0f68cd04899a2a04f2cbf75d0d760dcdfa2cd405 Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Sun, 12 May 2024 20:11:16 +0200 Subject: [PATCH 05/12] TASK: Move `TetheredNodeNotConfigured` exception to correct place --- Neos.ContentRepository.Core/Classes/NodeType/NodeType.php | 1 - .../Classes/NodeType/NodeTypeManager.php | 2 +- .../Exception/TetheredNodeNotConfigured.php | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) rename Neos.ContentRepository.Core/Classes/{NodeType => SharedModel}/Exception/TetheredNodeNotConfigured.php (75%) diff --git a/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php b/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php index 11409b0ada1..8ab04f134c3 100644 --- a/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php +++ b/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php @@ -14,7 +14,6 @@ * source code. */ -use Neos\ContentRepository\Core\NodeType\Exception\TetheredNodeNotConfigured; use Neos\ContentRepository\Core\SharedModel\Exception\InvalidNodeTypePostprocessorException; use Neos\ContentRepository\Core\SharedModel\Exception\NodeConfigurationException; use Neos\ContentRepository\Core\SharedModel\Node\NodeName; diff --git a/Neos.ContentRepository.Core/Classes/NodeType/NodeTypeManager.php b/Neos.ContentRepository.Core/Classes/NodeType/NodeTypeManager.php index 4d61d6a7312..28210f40846 100644 --- a/Neos.ContentRepository.Core/Classes/NodeType/NodeTypeManager.php +++ b/Neos.ContentRepository.Core/Classes/NodeType/NodeTypeManager.php @@ -14,7 +14,7 @@ namespace Neos\ContentRepository\Core\NodeType; -use Neos\ContentRepository\Core\NodeType\Exception\TetheredNodeNotConfigured; +use Neos\ContentRepository\Core\SharedModel\Exception\TetheredNodeNotConfigured; use Neos\ContentRepository\Core\SharedModel\Exception\NodeConfigurationException; use Neos\ContentRepository\Core\SharedModel\Exception\NodeTypeIsFinalException; use Neos\ContentRepository\Core\SharedModel\Exception\NodeTypeNotFoundException; diff --git a/Neos.ContentRepository.Core/Classes/NodeType/Exception/TetheredNodeNotConfigured.php b/Neos.ContentRepository.Core/Classes/SharedModel/Exception/TetheredNodeNotConfigured.php similarity index 75% rename from Neos.ContentRepository.Core/Classes/NodeType/Exception/TetheredNodeNotConfigured.php rename to Neos.ContentRepository.Core/Classes/SharedModel/Exception/TetheredNodeNotConfigured.php index 00f6c4fd3e1..5741f5beefe 100644 --- a/Neos.ContentRepository.Core/Classes/NodeType/Exception/TetheredNodeNotConfigured.php +++ b/Neos.ContentRepository.Core/Classes/SharedModel/Exception/TetheredNodeNotConfigured.php @@ -1,6 +1,6 @@ Date: Mon, 13 May 2024 20:30:58 +0200 Subject: [PATCH 06/12] TASK: Rename to `NodeTypeManager::getNodeTypeOfTetheredNode` --- .../Classes/NodeType/NodeTypeManager.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Neos.ContentRepository.Core/Classes/NodeType/NodeTypeManager.php b/Neos.ContentRepository.Core/Classes/NodeType/NodeTypeManager.php index 28210f40846..6f0347b0c72 100644 --- a/Neos.ContentRepository.Core/Classes/NodeType/NodeTypeManager.php +++ b/Neos.ContentRepository.Core/Classes/NodeType/NodeTypeManager.php @@ -196,7 +196,7 @@ public function overrideNodeTypes(array $completeNodeTypeConfiguration): void * @return NodeType * @throws TetheredNodeNotConfigured if the requested tethered node is not configured. Check via {@see NodeType::hasTetheredNode()}. */ - public function getTypeOfTetheredNode(NodeTypeName $nodeTypeName, NodeName $tetheredNodeName): NodeType + public function getNodeTypeOfTetheredNode(NodeTypeName $nodeTypeName, NodeName $tetheredNodeName): NodeType { $nodeType = $this->requireNodeType($nodeTypeName); $nameOfTetheredNode = $nodeType->getNodeTypeNameOfTetheredNode($tetheredNodeName); @@ -207,6 +207,7 @@ public function getTypeOfTetheredNode(NodeTypeName $nodeTypeName, NodeName $teth } /** + * Return an array with child nodes which should be automatically created * Return an array with child nodes which should be automatically created * * @return array the key of this array is the name of the child, and the value its NodeType. @@ -238,7 +239,7 @@ public function getTetheredNodesConfigurationForNodeType(NodeTypeName $nodeTypeN public function isNodeTypeAllowedAsChildToTetheredNode(NodeTypeName $parentNodeTypeName, NodeName $tetheredNodeName, NodeTypeName $nodeTypeName): bool { try { - $typeOfTetheredNode = $this->getTypeOfTetheredNode($parentNodeTypeName, $tetheredNodeName); + $typeOfTetheredNode = $this->getNodeTypeOfTetheredNode($parentNodeTypeName, $tetheredNodeName); } catch (TetheredNodeNotConfigured $exception) { throw new \InvalidArgumentException( sprintf( From e8de7a3e095f29568b3e58deb91a5a27301f275e Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Tue, 14 May 2024 19:50:37 +0200 Subject: [PATCH 07/12] TASK: Make NodeTypeManager::isNodeTypeAllowedAsChildToTetheredNode not throw an exception --- .../Classes/NodeType/NodeTypeManager.php | 47 ++++++++++--------- .../DisallowedChildNodeAdjustment.php | 14 +++--- 2 files changed, 30 insertions(+), 31 deletions(-) diff --git a/Neos.ContentRepository.Core/Classes/NodeType/NodeTypeManager.php b/Neos.ContentRepository.Core/Classes/NodeType/NodeTypeManager.php index 6f0347b0c72..d4f7707447b 100644 --- a/Neos.ContentRepository.Core/Classes/NodeType/NodeTypeManager.php +++ b/Neos.ContentRepository.Core/Classes/NodeType/NodeTypeManager.php @@ -225,48 +225,49 @@ public function getTetheredNodesConfigurationForNodeType(NodeTypeName $nodeTypeN } /** - * Checks if the given $nodeType is allowed as a childNode of the given $tetheredNodeName - * (which must be tethered in $parentNodeType). + * Checks if the given $nodeTypeNameToCheck is allowed as a childNode of the given $tetheredNodeName. * - * Only allowed to be called if $tetheredNodeName is actually tethered. + * Returns false if $tetheredNodeName is not tethered to $parentNodeTypeName, or if any of the $nodeTypeNameToCheck does not exist. * * @param NodeTypeName $parentNodeTypeName The NodeType to check constraints based on. * @param NodeName $tetheredNodeName The name of a configured tethered node of this NodeType - * @param NodeTypeName $nodeTypeName The NodeType to check constraints for. - * @return bool true if the $nodeType is allowed as grandchild node, false otherwise. - * @throws \InvalidArgumentException if the requested tethered node is not configured in the parent NodeType. Check via {@see NodeType::hasTetheredNode()}. + * @param NodeTypeName $nodeTypeNameToCheck The NodeType to check constraints for. + * @return bool true if the $nodeTypeNameToCheck is allowed as grandchild node, false otherwise. */ - public function isNodeTypeAllowedAsChildToTetheredNode(NodeTypeName $parentNodeTypeName, NodeName $tetheredNodeName, NodeTypeName $nodeTypeName): bool + public function isNodeTypeAllowedAsChildToTetheredNode(NodeTypeName $parentNodeTypeName, NodeName $tetheredNodeName, NodeTypeName $nodeTypeNameToCheck): bool { - try { - $typeOfTetheredNode = $this->getNodeTypeOfTetheredNode($parentNodeTypeName, $tetheredNodeName); - } catch (TetheredNodeNotConfigured $exception) { - throw new \InvalidArgumentException( - sprintf( - 'Cannot determine if grandchild is allowed in %s. Because the given child node name "%s" is not auto-created.', - $parentNodeTypeName->value, - $tetheredNodeName->value - ), - 1403858395, - $exception - ); + $parentNodeType = $this->getNodeType($parentNodeTypeName); + $nodeTypeNameOfTetheredNode = $parentNodeType?->getNodeTypeNameOfTetheredNode($tetheredNodeName); + if (!$parentNodeType || !$nodeTypeNameOfTetheredNode) { + // Cannot determine if grandchild is allowed, because the given child node name is not auto-created. + return false; + } + + $nodeTypeOfTetheredNode = $this->getNodeType($nodeTypeNameOfTetheredNode); + if (!$nodeTypeOfTetheredNode) { + return false; } // Constraints configured on the NodeType for the child node - $constraints = $typeOfTetheredNode->getConfiguration('constraints.nodeTypes') ?: []; + $constraints = $nodeTypeOfTetheredNode->getConfiguration('constraints.nodeTypes') ?: []; // Constraints configured at the childNode configuration of the parent. try { - $childNodeConstraintConfiguration = $this->requireNodeType($parentNodeTypeName)->getConfiguration('childNodes.' . $tetheredNodeName->value . '.constraints.nodeTypes') ?? []; + $childNodeConstraintConfiguration = $parentNodeType->getConfiguration('childNodes.' . $tetheredNodeName->value . '.constraints.nodeTypes') ?? []; } catch (PropertyNotAccessibleException $exception) { - // We ignore this because the configuration might just not have any constraints, if the childNode was not configured the exception above would have been thrown. + // We ignore this because the configuration might just not have any constraints, if the childNode was not configured null would have been returned. $childNodeConstraintConfiguration = []; } $constraints = Arrays::arrayMergeRecursiveOverrule($constraints, $childNodeConstraintConfiguration); + $nodeTypeToCheck = $this->getNodeType($nodeTypeNameToCheck); + if (!$nodeTypeToCheck) { + return false; + } + return ConstraintCheck::create($constraints)->isNodeTypeAllowed( - $this->requireNodeType($nodeTypeName) + $nodeTypeToCheck ); } diff --git a/Neos.ContentRepository.StructureAdjustment/src/Adjustment/DisallowedChildNodeAdjustment.php b/Neos.ContentRepository.StructureAdjustment/src/Adjustment/DisallowedChildNodeAdjustment.php index e3febf174f8..382387bfe78 100644 --- a/Neos.ContentRepository.StructureAdjustment/src/Adjustment/DisallowedChildNodeAdjustment.php +++ b/Neos.ContentRepository.StructureAdjustment/src/Adjustment/DisallowedChildNodeAdjustment.php @@ -75,15 +75,13 @@ public function findAdjustmentsForNodeType(NodeTypeName $nodeTypeName): \Generat $parentNode !== null && $grandparentNode !== null && $parentNode->classification->isTethered() - && !is_null($parentNode->nodeName) + && !is_null($parentNode->name) ) { - if ($this->nodeTypeManager->hasNodeType($grandparentNode->nodeTypeName)) { - $allowedByGrandparent = $this->nodeTypeManager->isNodeTypeAllowedAsChildToTetheredNode( - $grandparentNode->nodeTypeName, - $parentNode->nodeName, - $nodeAggregate->nodeTypeName - ); - } + $allowedByGrandparent = $this->nodeTypeManager->isNodeTypeAllowedAsChildToTetheredNode( + $grandparentNode->nodeTypeName, + $parentNode->name, + $nodeAggregate->nodeTypeName + ); } if (!$allowedByParent && !$allowedByGrandparent) { From 1e8c4e70d144551881f2f49d90c7b056664473e0 Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Tue, 14 May 2024 19:52:09 +0200 Subject: [PATCH 08/12] TASK: Remove NodeTypeManager::getTypeOfTetheredNode --- .../Classes/NodeType/NodeTypeManager.php | 17 ----------------- .../Exception/TetheredNodeNotConfigured.php | 10 ---------- 2 files changed, 27 deletions(-) delete mode 100644 Neos.ContentRepository.Core/Classes/SharedModel/Exception/TetheredNodeNotConfigured.php diff --git a/Neos.ContentRepository.Core/Classes/NodeType/NodeTypeManager.php b/Neos.ContentRepository.Core/Classes/NodeType/NodeTypeManager.php index d4f7707447b..ae0fcd08efa 100644 --- a/Neos.ContentRepository.Core/Classes/NodeType/NodeTypeManager.php +++ b/Neos.ContentRepository.Core/Classes/NodeType/NodeTypeManager.php @@ -14,7 +14,6 @@ namespace Neos\ContentRepository\Core\NodeType; -use Neos\ContentRepository\Core\SharedModel\Exception\TetheredNodeNotConfigured; use Neos\ContentRepository\Core\SharedModel\Exception\NodeConfigurationException; use Neos\ContentRepository\Core\SharedModel\Exception\NodeTypeIsFinalException; use Neos\ContentRepository\Core\SharedModel\Exception\NodeTypeNotFoundException; @@ -190,22 +189,6 @@ public function overrideNodeTypes(array $completeNodeTypeConfiguration): void } } - /** - * @param NodeTypeName $nodeTypeName - * @param NodeName $tetheredNodeName - * @return NodeType - * @throws TetheredNodeNotConfigured if the requested tethered node is not configured. Check via {@see NodeType::hasTetheredNode()}. - */ - public function getNodeTypeOfTetheredNode(NodeTypeName $nodeTypeName, NodeName $tetheredNodeName): NodeType - { - $nodeType = $this->requireNodeType($nodeTypeName); - $nameOfTetheredNode = $nodeType->getNodeTypeNameOfTetheredNode($tetheredNodeName); - if ($nameOfTetheredNode === null) { - throw new TetheredNodeNotConfigured(sprintf('The child node "%s" is not configured for node type "%s"', $tetheredNodeName->value, $nodeTypeName->value), 1694786811); - } - return $this->requireNodeType($nameOfTetheredNode); - } - /** * Return an array with child nodes which should be automatically created * Return an array with child nodes which should be automatically created diff --git a/Neos.ContentRepository.Core/Classes/SharedModel/Exception/TetheredNodeNotConfigured.php b/Neos.ContentRepository.Core/Classes/SharedModel/Exception/TetheredNodeNotConfigured.php deleted file mode 100644 index 5741f5beefe..00000000000 --- a/Neos.ContentRepository.Core/Classes/SharedModel/Exception/TetheredNodeNotConfigured.php +++ /dev/null @@ -1,10 +0,0 @@ - Date: Tue, 14 May 2024 20:08:39 +0200 Subject: [PATCH 09/12] FEATURE: Introduce `TetheredNodeTypeDefinitions` VO --- .../Classes/NodeType/NodeType.php | 24 ++++-- .../Classes/NodeType/NodeTypeManager.php | 2 +- .../NodeType/TetheredNodeTypeDefinition.php | 29 +++++++ .../NodeType/TetheredNodeTypeDefinitions.php | 78 +++++++++++++++++++ 4 files changed, 124 insertions(+), 9 deletions(-) create mode 100644 Neos.ContentRepository.Core/Classes/NodeType/TetheredNodeTypeDefinition.php create mode 100644 Neos.ContentRepository.Core/Classes/NodeType/TetheredNodeTypeDefinitions.php diff --git a/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php b/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php index 8ab04f134c3..45d31db32a9 100644 --- a/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php +++ b/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php @@ -73,6 +73,8 @@ final class NodeType */ protected bool $initialized = false; + private ?TetheredNodeTypeDefinitions $tetheredNodeTypeDefinitions = null; + /** * @param NodeTypeName $name Name of the node type * @param array $declaredSuperTypes Parent types instances of this node type, if null it should be unset @@ -491,21 +493,27 @@ public function getDefaultValuesForProperties(): array */ public function hasTetheredNode(NodeName $nodeName): bool { - $this->initialize(); - return (bool)$this->getNodeTypeNameOfTetheredNode($nodeName); + return $this->getTetheredNodeTypeDefinitions()->contain($nodeName); } - public function getNodeTypeNameOfTetheredNode(NodeName $nodeName): ?NodeTypeName + public function getTetheredNodeTypeDefinitions(): TetheredNodeTypeDefinitions { + if ($this->tetheredNodeTypeDefinitions) { + return $this->tetheredNodeTypeDefinitions; + } $this->initialize(); - foreach ($this->fullConfiguration['childNodes'] ?? [] as $rawChildNodeName => $configurationForChildNode) { + + $childNodeConfiguration = $this->getConfiguration('childNodes') ?? []; + $tetheredNodeTypeDefinitions = []; + foreach ($childNodeConfiguration as $childNodeName => $configurationForChildNode) { if (isset($configurationForChildNode['type'])) { - if (NodeName::transliterateFromString($rawChildNodeName)->equals($nodeName)) { - return NodeTypeName::fromString($configurationForChildNode['type']); - } + $tetheredNodeTypeDefinitions[] = new TetheredNodeTypeDefinition( + NodeName::transliterateFromString($childNodeName), + NodeTypeName::fromString($configurationForChildNode['type']) + ); } } - return null; + return $this->tetheredNodeTypeDefinitions = TetheredNodeTypeDefinitions::fromArray($tetheredNodeTypeDefinitions); } /** diff --git a/Neos.ContentRepository.Core/Classes/NodeType/NodeTypeManager.php b/Neos.ContentRepository.Core/Classes/NodeType/NodeTypeManager.php index ae0fcd08efa..c0e77a39091 100644 --- a/Neos.ContentRepository.Core/Classes/NodeType/NodeTypeManager.php +++ b/Neos.ContentRepository.Core/Classes/NodeType/NodeTypeManager.php @@ -220,7 +220,7 @@ public function getTetheredNodesConfigurationForNodeType(NodeTypeName $nodeTypeN public function isNodeTypeAllowedAsChildToTetheredNode(NodeTypeName $parentNodeTypeName, NodeName $tetheredNodeName, NodeTypeName $nodeTypeNameToCheck): bool { $parentNodeType = $this->getNodeType($parentNodeTypeName); - $nodeTypeNameOfTetheredNode = $parentNodeType?->getNodeTypeNameOfTetheredNode($tetheredNodeName); + $nodeTypeNameOfTetheredNode = $parentNodeType?->getTetheredNodeTypeDefinitions()->get($tetheredNodeName)?->nodeTypeName; if (!$parentNodeType || !$nodeTypeNameOfTetheredNode) { // Cannot determine if grandchild is allowed, because the given child node name is not auto-created. return false; diff --git a/Neos.ContentRepository.Core/Classes/NodeType/TetheredNodeTypeDefinition.php b/Neos.ContentRepository.Core/Classes/NodeType/TetheredNodeTypeDefinition.php new file mode 100644 index 00000000000..b3cc67b1ee7 --- /dev/null +++ b/Neos.ContentRepository.Core/Classes/NodeType/TetheredNodeTypeDefinition.php @@ -0,0 +1,29 @@ + + */ +final class TetheredNodeTypeDefinitions implements \IteratorAggregate +{ + /** + * @var array + */ + private array $tetheredNodeTypeDefinitions; + + private function __construct(TetheredNodeTypeDefinition ...$tetheredNodeTypeDefinitions) + { + /** @var array $tetheredNodeTypeDefinitions */ + $this->tetheredNodeTypeDefinitions = $tetheredNodeTypeDefinitions; + } + + /** + * @param array $tetheredNodeTypeDefinitions + */ + public static function fromArray(array $tetheredNodeTypeDefinitions): self + { + $tetheredNodeTypeDefinitionDefinitionByName = []; + foreach ($tetheredNodeTypeDefinitions as $index => $tetheredNodeTypeDefinitionDefinition) { + $tetheredNodeTypeDefinitionDefinition instanceof TetheredNodeTypeDefinition || throw new \InvalidArgumentException(sprintf('expected instance of %s, got: %s at index %s', TetheredNodeTypeDefinition::class, get_debug_type($tetheredNodeTypeDefinitionDefinition), $index), 1713549511); + !array_key_exists($tetheredNodeTypeDefinitionDefinition->name->value, $tetheredNodeTypeDefinitionDefinitionByName) || throw new \InvalidArgumentException(sprintf('Tethered node type definition with name "%s" is already registered at index %s', $tetheredNodeTypeDefinitionDefinition->name->value, $index), 1713549527); + $tetheredNodeTypeDefinitionDefinitionByName[$tetheredNodeTypeDefinitionDefinition->name->value] = $tetheredNodeTypeDefinitionDefinition; + } + return new self(...$tetheredNodeTypeDefinitionDefinitionByName); + } + + public function contain(NodeName|string $nodeName): bool + { + if ($nodeName instanceof NodeName) { + $nodeName = $nodeName->value; + } + return array_key_exists($nodeName, $this->tetheredNodeTypeDefinitions); + } + + public function get(NodeName|string $nodeName): ?TetheredNodeTypeDefinition + { + if ($nodeName instanceof NodeName) { + $nodeName = $nodeName->value; + } + return $this->tetheredNodeTypeDefinitions[$nodeName] ?? null; + } + + public function getIterator(): \Traversable + { + return yield from $this->tetheredNodeTypeDefinitions; + } + + /** + * @return array + */ + public function toArray(): array + { + return $this->tetheredNodeTypeDefinitions; + } +} From 5df367de5e0af377ca4201f568b659ef449f6545 Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Tue, 14 May 2024 20:19:07 +0200 Subject: [PATCH 10/12] TASK: Allow `NodeType::tetheredNodeTypeDefinitions` as forward compatible hack --- .../Feature/Common/ConstraintChecks.php | 4 +- .../Classes/NodeType/NodeType.php | 39 +++++++++++-------- .../Classes/NodeType/NodeTypeManager.php | 2 +- .../Classes/NodeDataToEventsProcessor.php | 2 +- .../DisallowedChildNodeAdjustment.php | 2 +- 5 files changed, 27 insertions(+), 22 deletions(-) diff --git a/Neos.ContentRepository.Core/Classes/Feature/Common/ConstraintChecks.php b/Neos.ContentRepository.Core/Classes/Feature/Common/ConstraintChecks.php index c7a064f4cc6..a57d8688b01 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/Common/ConstraintChecks.php +++ b/Neos.ContentRepository.Core/Classes/Feature/Common/ConstraintChecks.php @@ -223,7 +223,7 @@ protected function requireNodeTypeToDeclareReference(NodeTypeName $nodeTypeName, protected function requireNodeTypeNotToDeclareTetheredChildNodeName(NodeTypeName $nodeTypeName, NodeName $nodeName): void { $nodeType = $this->requireNodeType($nodeTypeName); - if ($nodeType->hasTetheredNode($nodeName)) { + if ($nodeType->tetheredNodeTypeDefinitions->contain($nodeName)) { throw new NodeNameIsAlreadyCovered( 'Node name "' . $nodeName->value . '" is reserved for a tethered child of parent node aggregate of type "' . $nodeTypeName->value . '".' @@ -369,7 +369,7 @@ protected function areNodeTypeConstraintsImposedByGrandparentValid( NodeType $nodeType ): bool { return !($parentNodeName - && $grandParentsNodeType->hasTetheredNode($parentNodeName) + && $grandParentsNodeType->tetheredNodeTypeDefinitions->contain($parentNodeName) && !$this->getNodeTypeManager()->isNodeTypeAllowedAsChildToTetheredNode($grandParentsNodeType->name, $parentNodeName, $nodeType->name)); } diff --git a/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php b/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php index 45d31db32a9..77c3e27fbfa 100644 --- a/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php +++ b/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php @@ -37,6 +37,9 @@ final class NodeType */ public readonly NodeTypeName $name; + /** @phpstan-ignore-next-line */ + public readonly TetheredNodeTypeDefinitions $tetheredNodeTypeDefinitions; + /** * Configuration for this node type, can be an arbitrarily nested array. Does not include inherited configuration. * @@ -73,8 +76,6 @@ final class NodeType */ protected bool $initialized = false; - private ?TetheredNodeTypeDefinitions $tetheredNodeTypeDefinitions = null; - /** * @param NodeTypeName $name Name of the node type * @param array $declaredSuperTypes Parent types instances of this node type, if null it should be unset @@ -102,6 +103,23 @@ public function __construct( } $this->localConfiguration = $configuration; + /** lazy properties {@see __get()} */ + /** @phpstan-ignore-next-line */ + unset($this->tetheredNodeTypeDefinitions); + } + + /** + * We unset the readonly properties in the constructor, so that this magic getter is invoked, which initializes the properties. + * {@see https://peakd.com/hive-168588/@crell/php-tricks-lazy-public-readonly-properties} + * This is a temporary hack until https://github.com/neos/neos-development-collection/pull/4999 is merged. + */ + public function __get(string $key): mixed + { + if ($key === 'tetheredNodeTypeDefinitions') { + /** @phpstan-ignore-next-line */ + return $this->tetheredNodeTypeDefinitions = $this->getTetheredNodeTypeDefinitions(); + } + throw new \BadMethodCallException(sprintf('NodeType::%s does not exist.', $key), 1715710576); } /** @@ -488,21 +506,8 @@ public function getDefaultValuesForProperties(): array return $defaultValues; } - /** - * @return bool true if $nodeName is an autocreated child node, false otherwise - */ - public function hasTetheredNode(NodeName $nodeName): bool - { - return $this->getTetheredNodeTypeDefinitions()->contain($nodeName); - } - - public function getTetheredNodeTypeDefinitions(): TetheredNodeTypeDefinitions + private function getTetheredNodeTypeDefinitions(): TetheredNodeTypeDefinitions { - if ($this->tetheredNodeTypeDefinitions) { - return $this->tetheredNodeTypeDefinitions; - } - $this->initialize(); - $childNodeConfiguration = $this->getConfiguration('childNodes') ?? []; $tetheredNodeTypeDefinitions = []; foreach ($childNodeConfiguration as $childNodeName => $configurationForChildNode) { @@ -513,7 +518,7 @@ public function getTetheredNodeTypeDefinitions(): TetheredNodeTypeDefinitions ); } } - return $this->tetheredNodeTypeDefinitions = TetheredNodeTypeDefinitions::fromArray($tetheredNodeTypeDefinitions); + return TetheredNodeTypeDefinitions::fromArray($tetheredNodeTypeDefinitions); } /** diff --git a/Neos.ContentRepository.Core/Classes/NodeType/NodeTypeManager.php b/Neos.ContentRepository.Core/Classes/NodeType/NodeTypeManager.php index c0e77a39091..a099ba73665 100644 --- a/Neos.ContentRepository.Core/Classes/NodeType/NodeTypeManager.php +++ b/Neos.ContentRepository.Core/Classes/NodeType/NodeTypeManager.php @@ -220,7 +220,7 @@ public function getTetheredNodesConfigurationForNodeType(NodeTypeName $nodeTypeN public function isNodeTypeAllowedAsChildToTetheredNode(NodeTypeName $parentNodeTypeName, NodeName $tetheredNodeName, NodeTypeName $nodeTypeNameToCheck): bool { $parentNodeType = $this->getNodeType($parentNodeTypeName); - $nodeTypeNameOfTetheredNode = $parentNodeType?->getTetheredNodeTypeDefinitions()->get($tetheredNodeName)?->nodeTypeName; + $nodeTypeNameOfTetheredNode = $parentNodeType?->tetheredNodeTypeDefinitions->get($tetheredNodeName)?->nodeTypeName; if (!$parentNodeType || !$nodeTypeNameOfTetheredNode) { // Cannot determine if grandchild is allowed, because the given child node name is not auto-created. return false; diff --git a/Neos.ContentRepository.LegacyNodeMigration/Classes/NodeDataToEventsProcessor.php b/Neos.ContentRepository.LegacyNodeMigration/Classes/NodeDataToEventsProcessor.php index bb80143d9db..005e12cf899 100644 --- a/Neos.ContentRepository.LegacyNodeMigration/Classes/NodeDataToEventsProcessor.php +++ b/Neos.ContentRepository.LegacyNodeMigration/Classes/NodeDataToEventsProcessor.php @@ -489,7 +489,7 @@ private function isAutoCreatedChildNode(NodeTypeName $parentNodeTypeName, NodeNa if (!$nodeTypeOfParent) { return false; } - return $nodeTypeOfParent->hasTetheredNode($nodeName); + return $nodeTypeOfParent->tetheredNodeTypeDefinitions->contain($nodeName); } private function dispatch(Severity $severity, string $message, mixed ...$args): void diff --git a/Neos.ContentRepository.StructureAdjustment/src/Adjustment/DisallowedChildNodeAdjustment.php b/Neos.ContentRepository.StructureAdjustment/src/Adjustment/DisallowedChildNodeAdjustment.php index 382387bfe78..a3743176f32 100644 --- a/Neos.ContentRepository.StructureAdjustment/src/Adjustment/DisallowedChildNodeAdjustment.php +++ b/Neos.ContentRepository.StructureAdjustment/src/Adjustment/DisallowedChildNodeAdjustment.php @@ -66,7 +66,7 @@ public function findAdjustmentsForNodeType(NodeTypeName $nodeTypeName): \Generat $parentNodeType = $this->nodeTypeManager->getNodeType($parentNode->nodeTypeName); if ($parentNodeType) { $allowedByParent = $parentNodeType->allowsChildNodeType($nodeType) - || ($nodeAggregate->nodeName && $parentNodeType->hasTetheredNode($nodeAggregate->nodeName)); + || ($nodeAggregate->nodeName && $parentNodeType->tetheredNodeTypeDefinitions->contain($nodeAggregate->nodeName)); } } From ab41496c4532e4baab65f74a03f04fc629a95bec Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Tue, 14 May 2024 21:07:07 +0200 Subject: [PATCH 11/12] TASK: Remove `NodeTypeManager::getTetheredNodesConfigurationForNodeType()` Previously known as `NodeType::getAutoCreatedChildNodes` --- .../Feature/Common/ConstraintChecks.php | 12 +++---- .../Feature/Common/TetheredNodeInternals.php | 28 ++++++++-------- .../Dto/NodeAggregateIdsByNodePaths.php | 11 +++++-- .../Feature/NodeCreation/NodeCreation.php | 23 +++++++------ .../Feature/NodeTypeChange/NodeTypeChange.php | 29 +++++++--------- .../RootNodeCreation/RootNodeHandling.php | 18 +++++----- .../Classes/NodeType/NodeTypeManager.php | 32 ------------------ .../NodeType/TetheredNodeTypeDefinitions.php | 2 +- .../Unit/NodeType/NodeTypeManagerTest.php | 5 ++- .../Adjustment/TetheredNodeAdjustments.php | 33 ++++++++----------- .../Classes/Service/NodeTypeSchemaBuilder.php | 8 ++--- 11 files changed, 83 insertions(+), 118 deletions(-) diff --git a/Neos.ContentRepository.Core/Classes/Feature/Common/ConstraintChecks.php b/Neos.ContentRepository.Core/Classes/Feature/Common/ConstraintChecks.php index a57d8688b01..6514d818b4a 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/Common/ConstraintChecks.php +++ b/Neos.ContentRepository.Core/Classes/Feature/Common/ConstraintChecks.php @@ -172,14 +172,13 @@ protected function requireRootNodeTypeToBeUnoccupied( /** * @param NodeType $nodeType - * @throws NodeTypeNotFoundException the configured child nodeType doesnt exist + * @throws NodeTypeNotFound the configured child nodeType doesnt exist */ protected function requireTetheredDescendantNodeTypesToExist(NodeType $nodeType): void { - // this getter throws if any of the child nodeTypes doesnt exist! - $tetheredNodeTypes = $this->getNodeTypeManager()->getTetheredNodesConfigurationForNodeType($nodeType->name); - foreach ($tetheredNodeTypes as $tetheredNodeType) { - $this->requireTetheredDescendantNodeTypesToExist($tetheredNodeType); + foreach ($nodeType->tetheredNodeTypeDefinitions as $tetheredNodeTypeDefinition) { + $nodeType = $this->requireNodeType($tetheredNodeTypeDefinition->nodeTypeName); + $this->requireTetheredDescendantNodeTypesToExist($nodeType); } } @@ -189,7 +188,8 @@ protected function requireTetheredDescendantNodeTypesToExist(NodeType $nodeType) */ protected function requireTetheredDescendantNodeTypesToNotBeOfTypeRoot(NodeType $nodeType): void { - foreach ($this->getNodeTypeManager()->getTetheredNodesConfigurationForNodeType($nodeType->name) as $tetheredChildNodeType) { + foreach ($nodeType->tetheredNodeTypeDefinitions as $tetheredNodeTypeDefinition) { + $tetheredChildNodeType = $this->requireNodeType($tetheredNodeTypeDefinition->nodeTypeName); if ($tetheredChildNodeType->isOfType(NodeTypeName::ROOT_NODE_TYPE_NAME)) { throw new NodeTypeIsOfTypeRoot( 'Node type "' . $nodeType->name->value . '" for tethered descendant is of type root.', diff --git a/Neos.ContentRepository.Core/Classes/Feature/Common/TetheredNodeInternals.php b/Neos.ContentRepository.Core/Classes/Feature/Common/TetheredNodeInternals.php index c99412cd353..36f27b2e80a 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/Common/TetheredNodeInternals.php +++ b/Neos.ContentRepository.Core/Classes/Feature/Common/TetheredNodeInternals.php @@ -20,15 +20,13 @@ use Neos\ContentRepository\Core\Feature\NodeModification\Dto\SerializedPropertyValues; use Neos\ContentRepository\Core\Feature\NodeVariation\Event\NodePeerVariantWasCreated; use Neos\ContentRepository\Core\Infrastructure\Property\PropertyConverter; -use Neos\ContentRepository\Core\NodeType\NodeType; use Neos\ContentRepository\Core\NodeType\NodeTypeName; +use Neos\ContentRepository\Core\NodeType\TetheredNodeTypeDefinition; use Neos\ContentRepository\Core\Projection\ContentGraph\ContentGraphInterface; use Neos\ContentRepository\Core\Projection\ContentGraph\Node; use Neos\ContentRepository\Core\Projection\ContentGraph\NodeAggregate; use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateClassification; use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId; -use Neos\ContentRepository\Core\SharedModel\Node\NodeName; -use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId; /** * @internal implementation details of command handlers @@ -58,15 +56,19 @@ protected function createEventsForMissingTetheredNode( ContentGraphInterface $contentGraph, NodeAggregate $parentNodeAggregate, OriginDimensionSpacePoint $originDimensionSpacePoint, - NodeName $tetheredNodeName, - ?NodeAggregateId $tetheredNodeAggregateId, - NodeType $expectedTetheredNodeType + TetheredNodeTypeDefinition $tetheredNodeTypeDefinition, + ?NodeAggregateId $tetheredNodeAggregateId ): Events { $childNodeAggregate = $contentGraph->findChildNodeAggregateByName( $parentNodeAggregate->nodeAggregateId, - $tetheredNodeName + $tetheredNodeTypeDefinition->name ); + $expectedTetheredNodeType = $this->nodeTypeManager->getNodeType($tetheredNodeTypeDefinition->nodeTypeName); + $defaultProperties = $expectedTetheredNodeType + ? SerializedPropertyValues::defaultFromNodeType($expectedTetheredNodeType, $this->getPropertyConverter()) + : SerializedPropertyValues::createEmpty(); + if ($childNodeAggregate === null) { // there is no tethered child node aggregate already; let's create it! $nodeType = $this->nodeTypeManager->getNodeType($parentNodeAggregate->nodeTypeName); @@ -91,14 +93,14 @@ protected function createEventsForMissingTetheredNode( $events[] = new NodeAggregateWithNodeWasCreated( $parentNodeAggregate->contentStreamId, $tetheredNodeAggregateId, - $expectedTetheredNodeType->name, + $tetheredNodeTypeDefinition->nodeTypeName, $rootGeneralizationOrigin, InterdimensionalSiblings::fromDimensionSpacePointSetWithoutSucceedingSiblings( $this->getInterDimensionalVariationGraph()->getSpecializationSet($rootGeneralization) ), $parentNodeAggregate->nodeAggregateId, - $tetheredNodeName, - SerializedPropertyValues::defaultFromNodeType($expectedTetheredNodeType, $this->getPropertyConverter()), + $tetheredNodeTypeDefinition->name, + $defaultProperties, NodeAggregateClassification::CLASSIFICATION_TETHERED, ); $creationOriginDimensionSpacePoint = $rootGeneralizationOrigin; @@ -110,14 +112,14 @@ protected function createEventsForMissingTetheredNode( new NodeAggregateWithNodeWasCreated( $parentNodeAggregate->contentStreamId, $tetheredNodeAggregateId ?: NodeAggregateId::create(), - $expectedTetheredNodeType->name, + $tetheredNodeTypeDefinition->nodeTypeName, $originDimensionSpacePoint, InterdimensionalSiblings::fromDimensionSpacePointSetWithoutSucceedingSiblings( $parentNodeAggregate->getCoverageByOccupant($originDimensionSpacePoint) ), $parentNodeAggregate->nodeAggregateId, - $tetheredNodeName, - SerializedPropertyValues::defaultFromNodeType($expectedTetheredNodeType, $this->getPropertyConverter()), + $tetheredNodeTypeDefinition->name, + $defaultProperties, NodeAggregateClassification::CLASSIFICATION_TETHERED, ) ); diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeCreation/Dto/NodeAggregateIdsByNodePaths.php b/Neos.ContentRepository.Core/Classes/Feature/NodeCreation/Dto/NodeAggregateIdsByNodePaths.php index d5eda29f1d9..083a4cfb607 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/NodeCreation/Dto/NodeAggregateIdsByNodePaths.php +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeCreation/Dto/NodeAggregateIdsByNodePaths.php @@ -18,6 +18,7 @@ use Neos\ContentRepository\Core\NodeType\NodeTypeManager; use Neos\ContentRepository\Core\NodeType\NodeTypeName; use Neos\ContentRepository\Core\Projection\ContentGraph\NodePath; +use Neos\ContentRepository\Core\SharedModel\Exception\NodeTypeNotFound; use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId; /** @@ -128,12 +129,16 @@ private static function createNodeAggregateIdsForNodeType( ?string $pathPrefix = null ): array { $nodeAggregateIds = []; - foreach ($nodeTypeManager->getTetheredNodesConfigurationForNodeType($nodeTypeName) as $nodeName => $childNodeType) { - $path = $pathPrefix ? $pathPrefix . '/' . $nodeName : $nodeName; + $nodeType = $nodeTypeManager->getNodeType($nodeTypeName); + if (!$nodeType) { + throw new NodeTypeNotFound(sprintf('Cannot build NodeAggregateIdsByNodePaths because NodeType %s does not exist.', $nodeTypeName->value), 1715711379); + } + foreach ($nodeType->tetheredNodeTypeDefinitions as $tetheredNodeTypeDefinition) { + $path = $pathPrefix ? $pathPrefix . '/' . $tetheredNodeTypeDefinition->name->value : $tetheredNodeTypeDefinition->name->value; $nodeAggregateIds[$path] = NodeAggregateId::create(); $nodeAggregateIds = array_merge( $nodeAggregateIds, - self::createNodeAggregateIdsForNodeType($childNodeType->name, $nodeTypeManager, $path) + self::createNodeAggregateIdsForNodeType($tetheredNodeTypeDefinition->nodeTypeName, $nodeTypeManager, $path) ); } diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeCreation/NodeCreation.php b/Neos.ContentRepository.Core/Classes/Feature/NodeCreation/NodeCreation.php index 6e8666fa09e..38fb29ef5a3 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/NodeCreation/NodeCreation.php +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeCreation/NodeCreation.php @@ -209,7 +209,7 @@ private function handleCreateNodeAggregateWithNodeAndSerializedProperties( array_push($events, ...iterator_to_array($this->handleTetheredChildNodes( $command, $contentGraph, - $command->nodeTypeName, + $nodeType, $coveredDimensionSpacePoints, $command->nodeAggregateId, $descendantNodeAggregateIds, @@ -257,30 +257,33 @@ private function createRegularWithNode( private function handleTetheredChildNodes( CreateNodeAggregateWithNodeAndSerializedProperties $command, ContentGraphInterface $contentGraph, - NodeTypeName $nodeTypeName, + NodeType $nodeType, DimensionSpacePointSet $coveredDimensionSpacePoints, NodeAggregateId $parentNodeAggregateId, NodeAggregateIdsByNodePaths $nodeAggregateIds, ?NodePath $nodePath ): Events { $events = []; - foreach ($this->getNodeTypeManager()->getTetheredNodesConfigurationForNodeType($nodeTypeName) as $nodeNameString => $childNodeType) { - $nodeName = NodeName::fromString($nodeNameString); + foreach ($nodeType->tetheredNodeTypeDefinitions as $tetheredNodeTypeDefinition) { + $childNodeType = $this->requireNodeType($tetheredNodeTypeDefinition->nodeTypeName); $childNodePath = $nodePath - ? $nodePath->appendPathSegment($nodeName) - : NodePath::fromString($nodeName->value); + ? $nodePath->appendPathSegment($tetheredNodeTypeDefinition->name) + : NodePath::fromString($tetheredNodeTypeDefinition->name->value); $childNodeAggregateId = $nodeAggregateIds->getNodeAggregateId($childNodePath) ?? NodeAggregateId::create(); - $initialPropertyValues = SerializedPropertyValues::defaultFromNodeType($childNodeType, $this->getPropertyConverter()); + $initialPropertyValues = SerializedPropertyValues::defaultFromNodeType( + $childNodeType, + $this->getPropertyConverter() + ); $events[] = new NodeAggregateWithNodeWasCreated( $contentGraph->getContentStreamId(), $childNodeAggregateId, - $childNodeType->name, + $tetheredNodeTypeDefinition->nodeTypeName, $command->originDimensionSpacePoint, InterdimensionalSiblings::fromDimensionSpacePointSetWithoutSucceedingSiblings($coveredDimensionSpacePoints), $parentNodeAggregateId, - $nodeName, + $tetheredNodeTypeDefinition->name, $initialPropertyValues, NodeAggregateClassification::CLASSIFICATION_TETHERED, ); @@ -288,7 +291,7 @@ private function handleTetheredChildNodes( array_push($events, ...iterator_to_array($this->handleTetheredChildNodes( $command, $contentGraph, - $childNodeType->name, + $childNodeType, $coveredDimensionSpacePoints, $childNodeAggregateId, $nodeAggregateIds, diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeTypeChange/NodeTypeChange.php b/Neos.ContentRepository.Core/Classes/Feature/NodeTypeChange/NodeTypeChange.php index 16219eb7160..72c20f0d7fc 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/NodeTypeChange/NodeTypeChange.php +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeTypeChange/NodeTypeChange.php @@ -28,8 +28,8 @@ use Neos\ContentRepository\Core\Feature\NodeTypeChange\Event\NodeAggregateTypeWasChanged; use Neos\ContentRepository\Core\NodeType\NodeType; use Neos\ContentRepository\Core\NodeType\NodeTypeManager; +use Neos\ContentRepository\Core\NodeType\TetheredNodeTypeDefinition; use Neos\ContentRepository\Core\Projection\ContentGraph\ContentGraphInterface; -use Neos\ContentRepository\Core\NodeType\NodeTypeName; use Neos\ContentRepository\Core\Projection\ContentGraph\Node; use Neos\ContentRepository\Core\Projection\ContentGraph\NodeAggregate; use Neos\ContentRepository\Core\Projection\ContentGraph\NodePath; @@ -90,9 +90,8 @@ abstract protected function createEventsForMissingTetheredNode( ContentGraphInterface $contentGraph, NodeAggregate $parentNodeAggregate, OriginDimensionSpacePoint $originDimensionSpacePoint, - NodeName $tetheredNodeName, - NodeAggregateId $tetheredNodeAggregateId, - NodeType $expectedTetheredNodeType + TetheredNodeTypeDefinition $tetheredNodeTypeDefinition, + NodeAggregateId $tetheredNodeAggregateId ): Events; /** @@ -177,36 +176,32 @@ private function handleChangeNodeAggregateType( array_push($events, ...iterator_to_array($this->deleteObsoleteTetheredNodesWhenChangingNodeType( $contentGraph, $nodeAggregate, - $command->newNodeTypeName + $newNodeType ))); } // new tethered child nodes - $expectedTetheredNodes = $this->getNodeTypeManager()->getTetheredNodesConfigurationForNodeType($command->newNodeTypeName); foreach ($nodeAggregate->getNodes() as $node) { assert($node instanceof Node); - foreach ($expectedTetheredNodes as $serializedTetheredNodeName => $expectedTetheredNodeType) { - $tetheredNodeName = NodeName::fromString($serializedTetheredNodeName); - + foreach ($newNodeType->tetheredNodeTypeDefinitions as $tetheredNodeTypeDefinition) { $tetheredNode = $contentGraph->getSubgraph( $node->originDimensionSpacePoint->toDimensionSpacePoint(), VisibilityConstraints::withoutRestrictions() )->findNodeByPath( - $tetheredNodeName, + $tetheredNodeTypeDefinition->name, $node->nodeAggregateId, ); if ($tetheredNode === null) { $tetheredNodeAggregateId = $command->tetheredDescendantNodeAggregateIds - ->getNodeAggregateId(NodePath::fromString($tetheredNodeName->value)) + ->getNodeAggregateId(NodePath::fromNodeNames($tetheredNodeTypeDefinition->name)) ?: NodeAggregateId::create(); array_push($events, ...iterator_to_array($this->createEventsForMissingTetheredNode( $contentGraph, $nodeAggregate, $node->originDimensionSpacePoint, - $tetheredNodeName, - $tetheredNodeAggregateId, - $expectedTetheredNodeType + $tetheredNodeTypeDefinition, + $tetheredNodeAggregateId ))); } } @@ -348,17 +343,15 @@ private function deleteDisallowedNodesWhenChangingNodeType( private function deleteObsoleteTetheredNodesWhenChangingNodeType( ContentGraphInterface $contentGraph, NodeAggregate $nodeAggregate, - NodeTypeName $newNodeTypeName + NodeType $newNodeType ): Events { - $expectedTetheredNodes = $this->getNodeTypeManager()->getTetheredNodesConfigurationForNodeType($newNodeTypeName); - $events = []; // find disallowed tethered nodes $tetheredNodeAggregates = $contentGraph->findTetheredChildNodeAggregates($nodeAggregate->nodeAggregateId); foreach ($tetheredNodeAggregates as $tetheredNodeAggregate) { /* @var $tetheredNodeAggregate NodeAggregate */ - if ($tetheredNodeAggregate->nodeName !== null && !isset($expectedTetheredNodes[$tetheredNodeAggregate->nodeName->value])) { + if ($tetheredNodeAggregate->nodeName !== null && !$newNodeType->tetheredNodeTypeDefinitions->contain($tetheredNodeAggregate->nodeName)) { // this aggregate (or parts thereof) are DISALLOWED according to constraints. // We now need to find out which edges we need to remove, $dimensionSpacePointsToBeRemoved = $this->findDimensionSpacePointsConnectingParentAndChildAggregate( diff --git a/Neos.ContentRepository.Core/Classes/Feature/RootNodeCreation/RootNodeHandling.php b/Neos.ContentRepository.Core/Classes/Feature/RootNodeCreation/RootNodeHandling.php index ab6f0a1b9a5..da442c82b68 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/RootNodeCreation/RootNodeHandling.php +++ b/Neos.ContentRepository.Core/Classes/Feature/RootNodeCreation/RootNodeHandling.php @@ -105,7 +105,7 @@ private function handleCreateRootNodeAggregateWithNode( foreach ($this->getInterDimensionalVariationGraph()->getRootGeneralizations() as $rootGeneralization) { array_push($events, ...iterator_to_array($this->handleTetheredRootChildNodes( $contentGraph->getContentStreamId(), - $command->nodeTypeName, + $nodeType, OriginDimensionSpacePoint::fromDimensionSpacePoint($rootGeneralization), $this->getInterDimensionalVariationGraph()->getSpecializationSet($rootGeneralization, true), $command->nodeAggregateId, @@ -184,7 +184,7 @@ private function handleUpdateRootNodeAggregateDimensions( */ private function handleTetheredRootChildNodes( ContentStreamId $contentStreamId, - NodeTypeName $nodeTypeName, + NodeType $nodeType, OriginDimensionSpacePoint $originDimensionSpacePoint, DimensionSpacePointSet $coveredDimensionSpacePoints, NodeAggregateId $parentNodeAggregateId, @@ -192,11 +192,11 @@ private function handleTetheredRootChildNodes( ?NodePath $nodePath ): Events { $events = []; - foreach ($this->getNodeTypeManager()->getTetheredNodesConfigurationForNodeType($nodeTypeName) as $nodeNameString => $childNodeType) { - $nodeName = NodeName::fromString($nodeNameString); + foreach ($nodeType->tetheredNodeTypeDefinitions as $tetheredNodeTypeDefinition) { + $childNodeType = $this->requireNodeType($tetheredNodeTypeDefinition->nodeTypeName); $childNodePath = $nodePath - ? $nodePath->appendPathSegment($nodeName) - : NodePath::fromString($nodeName->value); + ? $nodePath->appendPathSegment($tetheredNodeTypeDefinition->name) + : NodePath::fromNodeNames($tetheredNodeTypeDefinition->name); $childNodeAggregateId = $nodeAggregateIdsByNodePath->getNodeAggregateId($childNodePath) ?? NodeAggregateId::create(); $initialPropertyValues = SerializedPropertyValues::defaultFromNodeType($childNodeType, $this->getPropertyConverter()); @@ -204,17 +204,17 @@ private function handleTetheredRootChildNodes( $events[] = $this->createTetheredWithNodeForRoot( $contentStreamId, $childNodeAggregateId, - $childNodeType->name, + $tetheredNodeTypeDefinition->nodeTypeName, $originDimensionSpacePoint, $coveredDimensionSpacePoints, $parentNodeAggregateId, - $nodeName, + $tetheredNodeTypeDefinition->name, $initialPropertyValues ); array_push($events, ...iterator_to_array($this->handleTetheredRootChildNodes( $contentStreamId, - $childNodeType->name, + $childNodeType, $originDimensionSpacePoint, $coveredDimensionSpacePoints, $childNodeAggregateId, diff --git a/Neos.ContentRepository.Core/Classes/NodeType/NodeTypeManager.php b/Neos.ContentRepository.Core/Classes/NodeType/NodeTypeManager.php index a099ba73665..855a9433cab 100644 --- a/Neos.ContentRepository.Core/Classes/NodeType/NodeTypeManager.php +++ b/Neos.ContentRepository.Core/Classes/NodeType/NodeTypeManager.php @@ -189,24 +189,6 @@ public function overrideNodeTypes(array $completeNodeTypeConfiguration): void } } - /** - * Return an array with child nodes which should be automatically created - * Return an array with child nodes which should be automatically created - * - * @return array the key of this array is the name of the child, and the value its NodeType. - */ - public function getTetheredNodesConfigurationForNodeType(NodeTypeName $nodeTypeName): array - { - $childNodeConfiguration = $this->requireNodeType($nodeTypeName)->getConfiguration('childNodes'); - $autoCreatedChildNodes = []; - foreach ($childNodeConfiguration ?? [] as $childNodeName => $configurationForChildNode) { - if (isset($configurationForChildNode['type'])) { - $autoCreatedChildNodes[NodeName::transliterateFromString($childNodeName)->value] = $this->requireNodeType($configurationForChildNode['type']); - } - } - return $autoCreatedChildNodes; - } - /** * Checks if the given $nodeTypeNameToCheck is allowed as a childNode of the given $tetheredNodeName. * @@ -369,18 +351,4 @@ private function evaluateSuperTypeConfiguration( return $superType; } - - /** - * Helper to throw if the NodeType doesn't exit - */ - private function requireNodeType(string|NodeTypeName $nodeTypeName): NodeType - { - return $this->getNodeType($nodeTypeName) ?? throw new NodeTypeNotFoundException( - sprintf( - 'The node type "%s" is not available', - $nodeTypeName instanceof NodeTypeName ? $nodeTypeName->value : $nodeTypeName - ), - 1316598370 - ); - } } diff --git a/Neos.ContentRepository.Core/Classes/NodeType/TetheredNodeTypeDefinitions.php b/Neos.ContentRepository.Core/Classes/NodeType/TetheredNodeTypeDefinitions.php index 5f8f8a1e682..17ba4edfc28 100644 --- a/Neos.ContentRepository.Core/Classes/NodeType/TetheredNodeTypeDefinitions.php +++ b/Neos.ContentRepository.Core/Classes/NodeType/TetheredNodeTypeDefinitions.php @@ -65,7 +65,7 @@ public function get(NodeName|string $nodeName): ?TetheredNodeTypeDefinition public function getIterator(): \Traversable { - return yield from $this->tetheredNodeTypeDefinitions; + yield from $this->tetheredNodeTypeDefinitions; } /** diff --git a/Neos.ContentRepository.Core/Tests/Unit/NodeType/NodeTypeManagerTest.php b/Neos.ContentRepository.Core/Tests/Unit/NodeType/NodeTypeManagerTest.php index 017274ad619..6eb8fb13a23 100644 --- a/Neos.ContentRepository.Core/Tests/Unit/NodeType/NodeTypeManagerTest.php +++ b/Neos.ContentRepository.Core/Tests/Unit/NodeType/NodeTypeManagerTest.php @@ -433,10 +433,9 @@ public function anInheritedNodeTypePropertyCanBeOverruledWithEmptyArray(): void */ public function getAutoCreatedChildNodesReturnsLowercaseNames() { - $parentNodeTypeName = NodeTypeName::fromString('Neos.ContentRepository.Testing:Page2'); - $autoCreatedChildNodes = $this->nodeTypeManager->getTetheredNodesConfigurationForNodeType($parentNodeTypeName); + $parentNodeType = $this->nodeTypeManager->getNodeType(NodeTypeName::fromString('Neos.ContentRepository.Testing:Page2')); // This is configured as "nodeName" above, but should be normalized to "nodename" - self::assertArrayHasKey('nodename', $autoCreatedChildNodes); + self::assertNotNull($parentNodeType->tetheredNodeTypeDefinitions->contain('nodename')); } /** diff --git a/Neos.ContentRepository.StructureAdjustment/src/Adjustment/TetheredNodeAdjustments.php b/Neos.ContentRepository.StructureAdjustment/src/Adjustment/TetheredNodeAdjustments.php index f58e4bb02f2..c3ce45a2aae 100644 --- a/Neos.ContentRepository.StructureAdjustment/src/Adjustment/TetheredNodeAdjustments.php +++ b/Neos.ContentRepository.StructureAdjustment/src/Adjustment/TetheredNodeAdjustments.php @@ -51,8 +51,6 @@ public function findAdjustmentsForNodeType(NodeTypeName $nodeTypeName): \Generat // In case we cannot find the expected tethered nodes, this fix cannot do anything. return; } - $expectedTetheredNodes = $this->nodeTypeManager->getTetheredNodesConfigurationForNodeType($nodeTypeName); - foreach ($this->projectedNodeIterator->nodeAggregatesOfType($nodeTypeName) as $nodeAggregate) { // find missing tethered nodes $foundMissingOrDisallowedTetheredNodes = false; @@ -63,14 +61,12 @@ public function findAdjustmentsForNodeType(NodeTypeName $nodeTypeName): \Generat : $nodeAggregate->occupiedDimensionSpacePoints; foreach ($originDimensionSpacePoints as $originDimensionSpacePoint) { - foreach ($expectedTetheredNodes as $tetheredNodeName => $expectedTetheredNodeType) { - $tetheredNodeName = NodeName::fromString($tetheredNodeName); - + foreach ($nodeType->tetheredNodeTypeDefinitions as $tetheredNodeTypeDefinition) { $tetheredNode = $this->projectedNodeIterator->contentGraph->getSubgraph( $originDimensionSpacePoint->toDimensionSpacePoint(), VisibilityConstraints::withoutRestrictions() )->findNodeByPath( - $tetheredNodeName, + $tetheredNodeTypeDefinition->name, $nodeAggregate->nodeAggregateId ); if ($tetheredNode === null) { @@ -82,15 +78,14 @@ public function findAdjustmentsForNodeType(NodeTypeName $nodeTypeName): \Generat $originDimensionSpacePoint, $nodeAggregate->nodeAggregateId, StructureAdjustment::TETHERED_NODE_MISSING, - 'The tethered child node "' . $tetheredNodeName->value . '" is missing.', - function () use ($nodeAggregate, $originDimensionSpacePoint, $tetheredNodeName, $expectedTetheredNodeType) { + 'The tethered child node "' . $tetheredNodeTypeDefinition->name->value . '" is missing.', + function () use ($nodeAggregate, $originDimensionSpacePoint, $tetheredNodeTypeDefinition) { $events = $this->createEventsForMissingTetheredNode( $this->projectedNodeIterator->contentGraph, $nodeAggregate, $originDimensionSpacePoint, - $tetheredNodeName, - null, - $expectedTetheredNodeType + $tetheredNodeTypeDefinition, + null ); $streamName = ContentStreamEventStreamName::fromContentStreamId($nodeAggregate->contentStreamId); @@ -103,7 +98,7 @@ function () use ($nodeAggregate, $originDimensionSpacePoint, $tetheredNodeName, ); } else { yield from $this->ensureNodeIsTethered($tetheredNode); - yield from $this->ensureNodeIsOfType($tetheredNode, $expectedTetheredNodeType); + yield from $this->ensureNodeIsOfType($tetheredNode, $tetheredNodeTypeDefinition->nodeTypeName); } } } @@ -114,7 +109,7 @@ function () use ($nodeAggregate, $originDimensionSpacePoint, $tetheredNodeName, ); foreach ($tetheredNodeAggregates as $tetheredNodeAggregate) { assert($tetheredNodeAggregate->nodeName !== null); // it's tethered! - if (!isset($expectedTetheredNodes[$tetheredNodeAggregate->nodeName->value])) { + if (!$nodeType->tetheredNodeTypeDefinitions->contain($tetheredNodeAggregate->nodeName)) { $foundMissingOrDisallowedTetheredNodes = true; yield StructureAdjustment::createForNodeAggregate( $tetheredNodeAggregate, @@ -142,7 +137,7 @@ function () use ($tetheredNodeAggregate) { } } - if (array_keys($actualTetheredChildNodes) !== array_keys($expectedTetheredNodes)) { + if (array_keys($actualTetheredChildNodes) !== array_keys($nodeType->tetheredNodeTypeDefinitions->toArray())) { // we need to re-order: We go from the last to the first yield StructureAdjustment::createForNodeIdentity( $nodeAggregate->contentStreamId, @@ -150,14 +145,14 @@ function () use ($tetheredNodeAggregate) { $nodeAggregate->nodeAggregateId, StructureAdjustment::TETHERED_NODE_WRONGLY_ORDERED, 'Tethered nodes wrongly ordered, expected: ' - . implode(', ', array_keys($expectedTetheredNodes)) + . implode(', ', array_keys($nodeType->tetheredNodeTypeDefinitions->toArray())) . ' - actual: ' . implode(', ', array_keys($actualTetheredChildNodes)), fn () => $this->reorderNodes( $nodeAggregate->contentStreamId, $nodeAggregate->getCoverageByOccupant($originDimensionSpacePoint), $actualTetheredChildNodes, - array_keys($expectedTetheredNodes) + array_keys($nodeType->tetheredNodeTypeDefinitions->toArray()) ) ); } @@ -183,13 +178,13 @@ private function ensureNodeIsTethered(Node $node): \Generator /** * @return \Generator */ - private function ensureNodeIsOfType(Node $node, NodeType $expectedNodeType): \Generator + private function ensureNodeIsOfType(Node $node, NodeTypeName $expectedNodeTypeName): \Generator { - if ($node->nodeTypeName->value !== $expectedNodeType->name->value) { + if ($node->nodeTypeName->value !== $expectedNodeTypeName->value) { yield StructureAdjustment::createForNode( $node, StructureAdjustment::TETHERED_NODE_TYPE_WRONG, - 'should be of type "' . $expectedNodeType->name->value . '", but was "' . $node->nodeTypeName->value . '".' + 'should be of type "' . $expectedNodeTypeName->value . '", but was "' . $node->nodeTypeName->value . '".' ); } } diff --git a/Neos.Neos/Classes/Service/NodeTypeSchemaBuilder.php b/Neos.Neos/Classes/Service/NodeTypeSchemaBuilder.php index 4f6d755a356..a2834140522 100644 --- a/Neos.Neos/Classes/Service/NodeTypeSchemaBuilder.php +++ b/Neos.Neos/Classes/Service/NodeTypeSchemaBuilder.php @@ -109,10 +109,10 @@ protected function generateConstraints() } } - foreach ($this->nodeTypeManager->getTetheredNodesConfigurationForNodeType($nodeType->name) as $key => $_x) { - foreach ($nodeTypes as $innerNodeTypeName => $_y) { - if ($this->nodeTypeManager->isNodeTypeAllowedAsChildToTetheredNode($nodeType->name, NodeName::fromString($key), NodeTypeName::fromString($innerNodeTypeName))) { - $constraints[$nodeTypeName]['childNodes'][$key]['nodeTypes'][$innerNodeTypeName] = true; + foreach ($nodeType->tetheredNodeTypeDefinitions as $tetheredNodeTypeDefinition) { + foreach ($nodeTypes as $innerNodeTypeName => $_x) { + if ($this->nodeTypeManager->isNodeTypeAllowedAsChildToTetheredNode($nodeType->name, $tetheredNodeTypeDefinition->name, NodeTypeName::fromString($innerNodeTypeName))) { + $constraints[$nodeTypeName]['childNodes'][$tetheredNodeTypeDefinition->name->value]['nodeTypes'][$innerNodeTypeName] = true; } } } From 95ad9f1bea8f1b353c5c20a2a491c117a4e82a47 Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Fri, 17 May 2024 14:24:11 +0200 Subject: [PATCH 12/12] TASK: Correctly communicate `NodeTypeNotFound` nodetype manager behaviour --- .../Classes/NodeType/NodeTypeManager.php | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Neos.ContentRepository.Core/Classes/NodeType/NodeTypeManager.php b/Neos.ContentRepository.Core/Classes/NodeType/NodeTypeManager.php index cec5bd900a5..75cef7fa24f 100644 --- a/Neos.ContentRepository.Core/Classes/NodeType/NodeTypeManager.php +++ b/Neos.ContentRepository.Core/Classes/NodeType/NodeTypeManager.php @@ -16,7 +16,6 @@ use Neos\ContentRepository\Core\SharedModel\Exception\NodeConfigurationException; use Neos\ContentRepository\Core\SharedModel\Exception\NodeTypeIsFinalException; -use Neos\ContentRepository\Core\SharedModel\Exception\NodeTypeNotFound; use Neos\ContentRepository\Core\SharedModel\Node\NodeName; use Neos\Utility\Arrays; use Neos\Utility\Exception\PropertyNotAccessibleException; @@ -106,8 +105,6 @@ public function getSubNodeTypes(string|NodeTypeName $superTypeName, bool $includ /** * Returns the specified node type (which could be abstract) - * - * @throws NodeTypeNotFound */ public function getNodeType(string|NodeTypeName $nodeTypeName): ?NodeType { @@ -242,7 +239,6 @@ public function isNodeTypeAllowedAsChildToTetheredNode(NodeTypeName $parentNodeT * @param array $completeNodeTypeConfiguration the full node type configuration for all node types * @throws NodeConfigurationException * @throws NodeTypeIsFinalException - * @throws NodeTypeNotFound */ private function loadNodeType(string $nodeTypeName, array &$completeNodeTypeConfiguration): NodeType { @@ -251,7 +247,8 @@ private function loadNodeType(string $nodeTypeName, array &$completeNodeTypeConf } if (!isset($completeNodeTypeConfiguration[$nodeTypeName])) { - throw new NodeTypeNotFound('Node type "' . $nodeTypeName . '" does not exist', 1316451800); + // only thrown if a programming error occurred. + throw new \RuntimeException('Must not happen, logic error: Node type "' . $nodeTypeName . '" does not exist', 1316451800); } $nodeTypeConfiguration = $completeNodeTypeConfiguration[$nodeTypeName];