Skip to content

Commit

Permalink
!!!TASK: Remove NodeTypeManager from NodeType
Browse files Browse the repository at this point in the history
Disentangle NodeTypeManager from NodeType.

This is breaking because it removes API methods.

Resolves: #4515
  • Loading branch information
kitsunet committed Sep 15, 2023
1 parent 56aef70 commit ed2bb0f
Show file tree
Hide file tree
Showing 13 changed files with 287 additions and 240 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ protected function requireRootNodeTypeToBeUnoccupied(
*/
protected function requireTetheredDescendantNodeTypesToExist(NodeType $nodeType): void
{
foreach ($nodeType->getAutoCreatedChildNodes() as $childNodeType) {
foreach ($this->getNodeTypeManager()->getAutoCreatedChildNodesFor($nodeType) as $childNodeType) {
$this->requireTetheredDescendantNodeTypesToExist($childNodeType);
}
}
Expand All @@ -174,7 +174,7 @@ protected function requireTetheredDescendantNodeTypesToExist(NodeType $nodeType)
*/
protected function requireTetheredDescendantNodeTypesToNotBeOfTypeRoot(NodeType $nodeType): void
{
foreach ($nodeType->getAutoCreatedChildNodes() as $tetheredChildNodeType) {
foreach ($this->getNodeTypeManager()->getAutoCreatedChildNodesFor($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 @@ -300,11 +300,11 @@ protected function requireNodeTypeConstraintsImposedByParentToBeMet(
if (
$nodeName
&& $parentsNodeType->hasAutoCreatedChildNode($nodeName)
&& !$parentsNodeType->getTypeOfAutoCreatedChildNode($nodeName)?->name->equals($nodeType->name)
&& !$this->getNodeTypeManager()->getTypeOfAutoCreatedChildNode($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()->getTypeOfAutoCreatedChildNode($parentsNodeType, $nodeName)->name->value
. '" for auto created child nodes for parent type "' . $parentsNodeType->name->value
. '" with name "' . $nodeName->value . '"'
);
Expand All @@ -323,7 +323,7 @@ protected function areNodeTypeConstraintsImposedByParentValid(
if (
$nodeName
&& $parentsNodeType->hasAutoCreatedChildNode($nodeName)
&& !$parentsNodeType->getTypeOfAutoCreatedChildNode($nodeName)?->name->equals($nodeType->name)
&& !$this->getNodeTypeManager()->getTypeOfAutoCreatedChildNode($parentsNodeType, $nodeName)->name->equals($nodeType->name)
) {
return false;
}
Expand Down Expand Up @@ -361,7 +361,7 @@ protected function areNodeTypeConstraintsImposedByGrandparentValid(
if (
$parentNodeName
&& $grandParentsNodeType->hasAutoCreatedChildNode($parentNodeName)
&& !$grandParentsNodeType->allowsGrandchildNodeType($parentNodeName->value, $nodeType)
&& !$this->getNodeTypeManager()->allowsGrandchildNodeType($grandParentsNodeType,$parentNodeName, $nodeType)
) {
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ protected function checkConstraintsImposedByAncestors(
if (
$nodeAggregate->nodeName
&& $parentsNodeType->hasAutoCreatedChildNode($nodeAggregate->nodeName)
&& $parentsNodeType->getTypeOfAutoCreatedChildNode($nodeAggregate->nodeName)?->name
&& $this->nodeTypeManager->getTypeOfAutoCreatedChildNode($parentsNodeType, $nodeAggregate->nodeName)->name
!== $command->newNodeTypeName->value
) {
throw new NodeConstraintException(
Expand All @@ -233,8 +233,9 @@ protected function checkConstraintsImposedByAncestors(
if (
$parentAggregate->nodeName
&& $grandParentsNodeType->hasAutoCreatedChildNode($parentAggregate->nodeName)
&& !$grandParentsNodeType->allowsGrandchildNodeType(
$parentAggregate->nodeName->value,
&& !$this->nodeTypeManager->allowsGrandchildNodeType(
$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 @@ -282,7 +285,7 @@ private function handleTetheredChildNodes(
ContentRepository $contentRepository,
): Events {
$events = [];
foreach ($nodeType->getAutoCreatedChildNodes() as $rawNodeName => $childNodeType) {
foreach ($this->getNodeTypeManager()->getAutoCreatedChildNodesFor($nodeType) as $rawNodeName => $childNodeType) {
assert($childNodeType instanceof NodeType);
$nodeName = NodeName::fromString($rawNodeName);
$childNodePath = $nodePath
Expand Down Expand Up @@ -343,14 +346,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->getAutoCreatedChildNodesFor($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 @@ -190,7 +193,7 @@ private function handleChangeNodeAggregateType(
}

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

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,9 @@
<?php
namespace Neos\ContentRepository\Core\NodeType\Exception;

/**
*
*/
class ChildNodeNotConfigured extends \DomainException
{
}
Loading

0 comments on commit ed2bb0f

Please sign in to comment.