Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

!!!TASK: Remove NodeTypeManager from NodeType #4520

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -164,9 +164,9 @@ protected function requireRootNodeTypeToBeUnoccupied(
protected function requireTetheredDescendantNodeTypesToExist(NodeType $nodeType): void
{
// this getter throws if any of the child nodeTypes doesnt exist!
$childNodeTypes = $nodeType->getAutoCreatedChildNodes();
foreach ($childNodeTypes as $childNodeType) {
$this->requireTetheredDescendantNodeTypesToExist($childNodeType);
$tetheredNodeTypes = $this->getNodeTypeManager()->getTetheredNodesConfigurationForNodeType($nodeType);
foreach ($tetheredNodeTypes as $tetheredNodeType) {
$this->requireTetheredDescendantNodeTypesToExist($tetheredNodeType);
}
}

Expand All @@ -176,7 +176,7 @@ protected function requireTetheredDescendantNodeTypesToExist(NodeType $nodeType)
*/
protected function requireTetheredDescendantNodeTypesToNotBeOfTypeRoot(NodeType $nodeType): void
{
foreach ($nodeType->getAutoCreatedChildNodes() as $tetheredChildNodeType) {
foreach ($this->getNodeTypeManager()->getTetheredNodesConfigurationForNodeType($nodeType) 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.',
Expand Down Expand Up @@ -301,12 +301,12 @@ protected function requireNodeTypeConstraintsImposedByParentToBeMet(
}
if (
$nodeName
&& $parentsNodeType->hasAutoCreatedChildNode($nodeName)
&& !$parentsNodeType->getTypeOfAutoCreatedChildNode($nodeName)?->name->equals($nodeType->name)
&& $parentsNodeType->hasTetheredNode($nodeName)
&& !$this->getNodeTypeManager()->getTypeOfTetheredNode($parentsNodeType, $nodeName)->name->equals($nodeType->name)
) {
throw new NodeConstraintException(
'Node type "' . $nodeType->name->value . '" does not match configured "'
. $parentsNodeType->getTypeOfAutoCreatedChildNode($nodeName)?->name->value
. $this->getNodeTypeManager()->getTypeOfTetheredNode($parentsNodeType, $nodeName)->name->value
. '" for auto created child nodes for parent type "' . $parentsNodeType->name->value
. '" with name "' . $nodeName->value . '"'
);
Expand All @@ -324,8 +324,8 @@ protected function areNodeTypeConstraintsImposedByParentValid(
}
if (
$nodeName
&& $parentsNodeType->hasAutoCreatedChildNode($nodeName)
&& !$parentsNodeType->getTypeOfAutoCreatedChildNode($nodeName)?->name->equals($nodeType->name)
&& $parentsNodeType->hasTetheredNode($nodeName)
&& !$this->getNodeTypeManager()->getTypeOfTetheredNode($parentsNodeType, $nodeName)->name->equals($nodeType->name)
) {
return false;
}
Expand Down Expand Up @@ -362,8 +362,8 @@ protected function areNodeTypeConstraintsImposedByGrandparentValid(
): bool {
if (
$parentNodeName
&& $grandParentsNodeType->hasAutoCreatedChildNode($parentNodeName)
&& !$grandParentsNodeType->allowsGrandchildNodeType($parentNodeName->value, $nodeType)
&& $grandParentsNodeType->hasTetheredNode($parentNodeName)
&& !$this->getNodeTypeManager()->isNodeTypeAllowedAsChildToTetheredNode($grandParentsNodeType, $parentNodeName, $nodeType)
) {
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,8 +211,8 @@ protected function checkConstraintsImposedByAncestors(
}
if (
$nodeAggregate->nodeName
&& $parentsNodeType->hasAutoCreatedChildNode($nodeAggregate->nodeName)
&& $parentsNodeType->getTypeOfAutoCreatedChildNode($nodeAggregate->nodeName)?->name
&& $parentsNodeType->hasTetheredNode($nodeAggregate->nodeName)
&& $this->nodeTypeManager->getTypeOfTetheredNode($parentsNodeType, $nodeAggregate->nodeName)->name
!== $command->newNodeTypeName->value
) {
throw new NodeConstraintException(
Expand All @@ -232,9 +232,10 @@ protected function checkConstraintsImposedByAncestors(
);
if (
$parentAggregate->nodeName
&& $grandParentsNodeType->hasAutoCreatedChildNode($parentAggregate->nodeName)
&& !$grandParentsNodeType->allowsGrandchildNodeType(
$parentAggregate->nodeName->value,
&& $grandParentsNodeType->hasTetheredNode($parentAggregate->nodeName)
&& !$this->nodeTypeManager->isNodeTypeAllowedAsChildToTetheredNode(
$grandParentsNodeType,
$parentAggregate->nodeName,
$newNodeType
)
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
use Neos\ContentRepository\Core\Infrastructure\Property\PropertyConverter;
use Neos\ContentRepository\Core\Infrastructure\Property\PropertyType;
use Neos\ContentRepository\Core\NodeType\NodeType;
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\ContentStreamDoesNotExistYet;
Expand Down Expand Up @@ -61,6 +62,8 @@ abstract protected function requireNodeTypeToBeOfTypeRoot(NodeType $nodeType): v

abstract protected function getPropertyConverter(): PropertyConverter;

abstract protected function getNodeTypeManager(): NodeTypeManager;

private function handleCreateNodeAggregateWithNode(
CreateNodeAggregateWithNode $command,
ContentRepository $contentRepository
Expand Down Expand Up @@ -203,6 +206,7 @@ private function handleCreateNodeAggregateWithNodeAndSerializedProperties(
}
$descendantNodeAggregateIds = self::populateNodeAggregateIds(
$nodeType,
$this->getNodeTypeManager(),
$command->tetheredDescendantNodeAggregateIds
);
// Write the auto-created descendant node aggregate ids back to the command;
Expand Down Expand Up @@ -282,7 +286,7 @@ private function handleTetheredChildNodes(
ContentRepository $contentRepository,
): Events {
$events = [];
foreach ($nodeType->getAutoCreatedChildNodes() as $rawNodeName => $childNodeType) {
foreach ($this->getNodeTypeManager()->getTetheredNodesConfigurationForNodeType($nodeType) as $rawNodeName => $childNodeType) {
assert($childNodeType instanceof NodeType);
$nodeName = NodeName::fromString($rawNodeName);
$childNodePath = $nodePath
Expand Down Expand Up @@ -343,14 +347,15 @@ private function createTetheredWithNode(

protected static function populateNodeAggregateIds(
NodeType $nodeType,
NodeTypeManager $nodeTypeManager,
?NodeAggregateIdsByNodePaths $nodeAggregateIds,
NodePath $childPath = null
): NodeAggregateIdsByNodePaths {
if ($nodeAggregateIds === null) {
$nodeAggregateIds = NodeAggregateIdsByNodePaths::createEmpty();
}
// TODO: handle Multiple levels of autocreated child nodes
foreach ($nodeType->getAutoCreatedChildNodes() as $rawChildName => $childNodeType) {
foreach ($nodeTypeManager->getTetheredNodesConfigurationForNodeType($nodeType) as $rawChildName => $childNodeType) {
$childName = NodeName::fromString($rawChildName);
$childPath = $childPath
? $childPath->appendPathSegment($childName)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
use Neos\ContentRepository\Core\Feature\NodeTypeChange\Command\ChangeNodeAggregateType;
use Neos\ContentRepository\Core\Feature\NodeTypeChange\Event\NodeAggregateTypeWasChanged;
use Neos\ContentRepository\Core\NodeType\NodeType;
use Neos\ContentRepository\Core\NodeType\NodeTypeManager;
use Neos\ContentRepository\Core\Projection\ContentGraph\Node;
use Neos\ContentRepository\Core\Projection\ContentGraph\NodeAggregate;
use Neos\ContentRepository\Core\Projection\ContentGraph\NodePath;
Expand All @@ -49,6 +50,8 @@
*/
trait NodeTypeChange
{
abstract protected function getNodeTypeManager(): NodeTypeManager;

abstract protected function requireProjectedNodeAggregate(
ContentStreamId $contentStreamId,
NodeAggregateId $nodeAggregateId,
Expand Down Expand Up @@ -89,6 +92,7 @@ abstract protected function areNodeTypeConstraintsImposedByGrandparentValid(

abstract protected static function populateNodeAggregateIds(
NodeType $nodeType,
NodeTypeManager $nodeTypeManager,
NodeAggregateIdsByNodePaths $nodeAggregateIds,
NodePath $childPath = null
): NodeAggregateIdsByNodePaths;
Expand Down Expand Up @@ -158,6 +162,7 @@ private function handleChangeNodeAggregateType(
**************/
$descendantNodeAggregateIds = static::populateNodeAggregateIds(
$newNodeType,
$this->getNodeTypeManager(),
$command->tetheredDescendantNodeAggregateIds
);
// Write the auto-created descendant node aggregate ids back to the command;
Expand Down Expand Up @@ -190,7 +195,7 @@ private function handleChangeNodeAggregateType(
}

// new tethered child nodes
$expectedTetheredNodes = $newNodeType->getAutoCreatedChildNodes();
$expectedTetheredNodes = $this->getNodeTypeManager()->getTetheredNodesConfigurationForNodeType($newNodeType);
foreach ($nodeAggregate->getNodes() as $node) {
assert($node instanceof Node);
foreach ($expectedTetheredNodes as $serializedTetheredNodeName => $expectedTetheredNodeType) {
Expand Down Expand Up @@ -371,7 +376,7 @@ private function deleteObsoleteTetheredNodesWhenChangingNodeType(
NodeType $newNodeType,
ContentRepository $contentRepository
): Events {
$expectedTetheredNodes = $newNodeType->getAutoCreatedChildNodes();
$expectedTetheredNodes = $this->getNodeTypeManager()->getTetheredNodesConfigurationForNodeType($newNodeType);

$events = [];
// find disallowed tethered nodes
Expand Down
137 changes: 137 additions & 0 deletions Neos.ContentRepository.Core/Classes/NodeType/ConstraintCheck.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
<?php

namespace Neos\ContentRepository\Core\NodeType;

/**
* Performs node type constraint checks against a given set of constraints
* @internal
*/
class ConstraintCheck
{
/**
* @param array<string,mixed> $constraints
*/
public function __construct(
private readonly array $constraints
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be neat to have this be a DTO with the constraints configuration.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes or at lest an example how this looks would be cool ^^

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 for DTO
Also there seem to be some overlaps with the NodeTypeConstraints DTO

) {
}

public function isNodeTypeAllowed(NodeType $nodeType): bool
{
$directConstraintsResult = $this->isNodeTypeAllowedByDirectConstraints($nodeType);
if ($directConstraintsResult !== null) {
return $directConstraintsResult;
}

$inheritanceConstraintsResult = $this->isNodeTypeAllowedByInheritanceConstraints($nodeType);
if ($inheritanceConstraintsResult !== null) {
return $inheritanceConstraintsResult;
}

if (isset($this->constraints['*'])) {
return (bool)$this->constraints['*'];
}

return false;
}

/**
* @return boolean|null true if the passed $nodeType is allowed by the $constraints, null if couldn't be decided
*/
protected function isNodeTypeAllowedByDirectConstraints(NodeType $nodeType): ?bool
{
if ($this->constraints === []) {
return true;
}

if (
array_key_exists($nodeType->name->value, $this->constraints)
&& $this->constraints[$nodeType->name->value] === true
) {
return true;
}

if (
array_key_exists($nodeType->name->value, $this->constraints)
&& $this->constraints[$nodeType->name->value] === false
) {
return false;
}

return null;
}

/**
* This method loops over the constraints and finds node types that the given node type inherits from. For all
* matched super types, their super types are traversed to find the closest super node with a constraint which
* is used to evaluated if the node type is allowed. It finds the closest results for true and false, and uses
* the distance to choose which one wins (lowest). If no result is found the node type is allowed.
*
* @return ?boolean (null if no constraint matched)
*/
protected function isNodeTypeAllowedByInheritanceConstraints(NodeType $nodeType): ?bool
{
$constraintDistanceForTrue = null;
$constraintDistanceForFalse = null;
foreach ($this->constraints as $superType => $constraint) {
if ($nodeType->isOfType($superType)) {
$distance = $this->traverseSuperTypes($nodeType, $superType, 0);

if (
$constraint === true
&& ($constraintDistanceForTrue === null || $constraintDistanceForTrue > $distance)
) {
$constraintDistanceForTrue = $distance;
}
if (
$constraint === false
&& ($constraintDistanceForFalse === null || $constraintDistanceForFalse > $distance)
) {
$constraintDistanceForFalse = $distance;
}
}
}

if ($constraintDistanceForTrue !== null && $constraintDistanceForFalse !== null) {
return $constraintDistanceForTrue < $constraintDistanceForFalse;
}

if ($constraintDistanceForFalse !== null) {
return false;
}

if ($constraintDistanceForTrue !== null) {
return true;
}

return null;
}

/**
* This method traverses the given node type to find the first super type that matches the constraint node type.
* In case the hierarchy has more than one way of finding a path to the node type it's not taken into account,
* since the first matched is returned. This is accepted on purpose for performance reasons and due to the fact
* that such hierarchies should be avoided.
*
* Returns null if no NodeType matched
*/
protected function traverseSuperTypes(
NodeType $currentNodeType,
string $constraintNodeTypeName,
int $distance
): ?int {
if ($currentNodeType->name->value === $constraintNodeTypeName) {
return $distance;
}

$distance++;
foreach ($currentNodeType->getDeclaredSuperTypes() as $superType) {
$result = $this->traverseSuperTypes($superType, $constraintNodeTypeName, $distance);
if ($result !== null) {
return $result;
}
}

return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace Neos\ContentRepository\Core\NodeType\Exception;

/**
* @api Might be encountered when childNode information is requested for a child node which was never configured.
*/
class TetheredNodeNotConfigured extends \DomainException
{
}
Loading
Loading