Skip to content

Commit

Permalink
Separate absolute from relative paths
Browse files Browse the repository at this point in the history
  • Loading branch information
Bernhard Schmitt committed Jun 19, 2023
1 parent 9e5add7 commit 2b4e5b6
Show file tree
Hide file tree
Showing 22 changed files with 355 additions and 166 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
use Neos\ContentRepository\Core\Infrastructure\DbalClientInterface;
use Neos\ContentRepository\Core\NodeType\NodeTypeManager;
use Neos\ContentRepository\Core\NodeType\NodeTypeName;
use Neos\ContentRepository\Core\Projection\ContentGraph\AbsoluteNodePath;
use Neos\ContentRepository\Core\Projection\ContentGraph\ContentSubgraphInterface;
use Neos\ContentRepository\Core\Projection\ContentGraph\Filter\CountBackReferencesFilter;
use Neos\ContentRepository\Core\Projection\ContentGraph\Filter\CountChildNodesFilter;
Expand Down Expand Up @@ -195,17 +196,26 @@ public function findParentNode(NodeAggregateId $childNodeAggregateId): ?Node
return $this->fetchNode($queryBuilder);
}

public function findNodeByPath(NodePath $path, ?NodeAggregateId $startingNodeAggregateId): ?Node
public function findNodeByPath(NodePath $path, NodeAggregateId $startingNodeAggregateId): ?Node
{
$currentNode = $this->findNodeById($startingNodeAggregateId);

return $currentNode
? $this->findNodeByPathFromStartingNode($currentNode, $path)
: null;
}

public function findNodeByAbsolutePath(AbsoluteNodePath $path): ?Node
{
$currentNode = $this->findRootNodeByType($path->rootNodeTypeName);

return $currentNode
? $this->findNodeByPathFromStartingNode($currentNode, $path->path)
: null;
}

private function findNodeByPathFromStartingNode(Node $currentNode, NodePath $path): ?Node
{
$currentNode = null;
if ($path->rootNodeTypeName) {
$currentNode = $this->findRootNodeByType($path->rootNodeTypeName);
} elseif ($startingNodeAggregateId) {
$currentNode = $this->findNodeById($startingNodeAggregateId);
}
if ($currentNode === null) {
return null;
}
foreach ($path->getParts() as $edgeName) {
// id exists here :)
$currentNode = $this->findChildNodeConnectedThroughEdgeName($currentNode->nodeAggregateId, $edgeName);
Expand Down Expand Up @@ -243,7 +253,7 @@ public function findPrecedingSiblingNodes(NodeAggregateId $siblingNodeAggregateI
return $this->fetchNodes($queryBuilder);
}

public function retrieveNodePath(NodeAggregateId $nodeAggregateId): NodePath
public function retrieveNodePath(NodeAggregateId $nodeAggregateId): AbsoluteNodePath
{
$queryBuilderInitial = $this->createQueryBuilder()
->select('h.name, h.parentnodeanchor, h.childnodeanchor')
Expand Down Expand Up @@ -307,7 +317,10 @@ public function retrieveNodePath(NodeAggregateId $nodeAggregateId): NodePath
}
}

return NodePath::fromPathSegments($pathSegments, $rootNodeTypeName);
return AbsoluteNodePath::fromComponents(
$rootNodeTypeName,
NodePath::fromPathSegments($pathSegments)
);
}

public function findSubtree(NodeAggregateId $entryNodeAggregateId, FindSubtreeFilter $filter): ?Subtree
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePoint;
use Neos\ContentRepository\Core\NodeType\NodeTypeManager;
use Neos\ContentRepository\Core\NodeType\NodeTypeName;
use Neos\ContentRepository\Core\Projection\ContentGraph\AbsoluteNodePath;
use Neos\ContentRepository\Core\Projection\ContentGraph\ContentSubgraphInterface;
use Neos\ContentRepository\Core\Projection\ContentGraph\Filter;
use Neos\ContentRepository\Core\Projection\ContentGraph\Filter\FindBackReferencesFilter;
Expand Down Expand Up @@ -254,30 +255,33 @@ public function findParentNode(NodeAggregateId $childNodeAggregateId): ?Node
) : null;
}

public function findNodeByPath(
NodePath $path,
?NodeAggregateId $startingNodeAggregateId
): ?Node {
$currentNode = null;
if ($path->rootNodeTypeName) {
$currentNode = $this->findRootNodeByType($path->rootNodeTypeName);
} elseif ($startingNodeAggregateId) {
$currentNode = $this->findNodeById($startingNodeAggregateId);
}
if ($currentNode === null) {
return null;
}
public function findNodeByPath(NodePath $path, NodeAggregateId $startingNodeAggregateId): ?Node
{
$currentNode = $this->findNodeById($startingNodeAggregateId);

return $currentNode
? $this->findNodeByPathFromStartingNode($currentNode, $path)
: null;
}

public function findNodeByAbsolutePath(AbsoluteNodePath $path): ?Node
{
$currentNode = $this->findRootNodeByType($path->rootNodeTypeName);

return $currentNode
? $this->findNodeByPathFromStartingNode($currentNode, $path->path)
: null;
}

private function findNodeByPathFromStartingNode(Node $currentNode, NodePath $path): ?Node
{
foreach ($path->getParts() as $edgeName) {
$currentNode = $this->findChildNodeConnectedThroughEdgeName(
$currentNode->nodeAggregateId,
$edgeName
);
if (!$currentNode) {
// id exists here :)
$currentNode = $this->findChildNodeConnectedThroughEdgeName($currentNode->nodeAggregateId, $edgeName);
if ($currentNode === null) {
return null;
}
}

return $currentNode;
}

Expand Down Expand Up @@ -360,9 +364,9 @@ private function findAnySiblings(
return $this->nodeFactory->mapNodeRowsToNodes($siblingsRows, $this->visibilityConstraints);
}

public function retrieveNodePath(NodeAggregateId $nodeAggregateId): NodePath
public function retrieveNodePath(NodeAggregateId $nodeAggregateId): AbsoluteNodePath
{
return NodePath::fromString('/');
return AbsoluteNodePath::fromString('/');
}

public function findSubtree(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public function __construct(array $nodeAggregateIds)
);
}

$this->nodeAggregateIds[$nodePath->__toString()] = $nodeAggregateId;
$this->nodeAggregateIds[$nodePath->serializeToString()] = $nodeAggregateId;
}
}

Expand Down Expand Up @@ -100,13 +100,13 @@ public function merge(self $other): self

public function getNodeAggregateId(NodePath $nodePath): ?NodeAggregateId
{
return $this->nodeAggregateIds[$nodePath->__toString()] ?? null;
return $this->nodeAggregateIds[$nodePath->serializeToString()] ?? null;
}

public function add(NodePath $nodePath, NodeAggregateId $nodeAggregateId): self
{
$nodeAggregateIds = $this->nodeAggregateIds;
$nodeAggregateIds[$nodePath->__toString()] = $nodeAggregateId;
$nodeAggregateIds[$nodePath->serializeToString()] = $nodeAggregateId;

return new self($nodeAggregateIds);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
<?php

/*
* This file is part of the Neos.ContentRepository package.
*
* (c) Contributors of the Neos Project - www.neos.io
*
* This package is Open Source Software. For the full copyright and license
* information, please view the LICENSE file which was distributed with this
* source code.
*/

declare(strict_types=1);

namespace Neos\ContentRepository\Core\Projection\ContentGraph;

use Neos\ContentRepository\Core\NodeType\NodeTypeName;
use Neos\ContentRepository\Core\SharedModel\Exception\AbsoluteNodePathIsInvalid;
use Neos\ContentRepository\Core\SharedModel\Node\NodeName;

/**
* An absolute node path, composed of the root node's type and a relative path from there.
*
* It describes the hierarchy path of a node to and including its root node in a subgraph.
* @api
*/
final class AbsoluteNodePath implements \JsonSerializable
{
private function __construct(
public readonly NodeTypeName $rootNodeTypeName,
public readonly NodePath $path
) {
}

public static function fromComponents(
NodeTypeName $rootNodeTypeName,
NodePath $path
): self {
return new self(
$rootNodeTypeName,
$path
);
}

public static function fromString(string $value): self
{
if (\str_starts_with($value, '/<')) {
$pivot = \mb_strpos($value, '>');
if ($pivot > 0) {
$nodeTypeName = NodeTypeName::fromString(\mb_substr($value, 2, $pivot - 2));
$path = \mb_substr($value, $pivot + 2) ?: '/';
} else {
throw AbsoluteNodePathIsInvalid::becauseItDoesNotMatchTheRequiredPattern($value);
}
} else {
throw AbsoluteNodePathIsInvalid::becauseItDoesNotMatchTheRequiredPattern($value);
}
return new self($nodeTypeName, NodePath::fromString($path));
}

public static function tryFromString(string $string): ?self
{
try {
return self::fromString($string);
} catch (AbsoluteNodePathIsInvalid) {
return null;
}
}

public function appendPathSegment(NodeName $nodeName): self
{
return new self(
$this->rootNodeTypeName,
$this->path->appendPathSegment($nodeName)
);
}

public function isRoot(): bool
{
return $this->path->value === '/';
}

/**
* @return array<int,NodeName>
*/
public function getParts(): array
{
return $this->path->getParts();
}

public function getDepth(): int
{
return count($this->path->getParts());
}

public function equals(AbsoluteNodePath $other): bool
{
return $this->path === $other->path
&& $this->rootNodeTypeName->equals($other->rootNodeTypeName);
}

public function serializeToString(): string
{
return rtrim('/<' . $this->rootNodeTypeName->value . '>/' . (ltrim($this->path->value, '/')), '/');
}

public function jsonSerialize(): string
{
return $this->serializeToString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
namespace Neos\ContentRepository\Core\Projection\ContentGraph\ContentGraphWithRuntimeCaches;

use Neos\ContentRepository\Core\NodeType\NodeTypeName;
use Neos\ContentRepository\Core\Projection\ContentGraph\AbsoluteNodePath;
use Neos\ContentRepository\Core\Projection\ContentGraph\ContentSubgraphInterface;
use Neos\ContentRepository\Core\Projection\ContentGraph\Filter;
use Neos\ContentRepository\Core\Projection\ContentGraph\Filter\CountBackReferencesFilter;
Expand Down Expand Up @@ -156,12 +157,18 @@ public function findParentNode(NodeAggregateId $childNodeAggregateId): ?Node
return $parentNode;
}

public function findNodeByPath(NodePath $path, ?NodeAggregateId $startingNodeAggregateId): ?Node
public function findNodeByPath(NodePath $path, NodeAggregateId $startingNodeAggregateId): ?Node
{
// TODO implement runtime caches
return $this->wrappedContentSubgraph->findNodeByPath($path, $startingNodeAggregateId);
}

public function findNodeByAbsolutePath(AbsoluteNodePath $path): ?Node
{
// TODO implement runtime caches
return $this->wrappedContentSubgraph->findNodeByAbsolutePath($path);
}

public function findChildNodeConnectedThroughEdgeName(NodeAggregateId $parentNodeAggregateId, NodeName $edgeName): ?Node
{
$namedChildNodeCache = $this->inMemoryCache->getNamedChildNodeByNodeIdCache();
Expand Down Expand Up @@ -189,7 +196,7 @@ public function findPrecedingSiblingNodes(NodeAggregateId $siblingNodeAggregateI
return $this->wrappedContentSubgraph->findPrecedingSiblingNodes($siblingNodeAggregateId, $filter);
}

public function retrieveNodePath(NodeAggregateId $nodeAggregateId): NodePath
public function retrieveNodePath(NodeAggregateId $nodeAggregateId): AbsoluteNodePath
{
$nodePathCache = $this->inMemoryCache->getNodePathCache();
$cachedNodePath = $nodePathCache->get($nodeAggregateId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@

namespace Neos\ContentRepository\Core\Projection\ContentGraph\ContentGraphWithRuntimeCaches\InMemoryCache;

use Neos\ContentRepository\Core\Projection\ContentGraph\AbsoluteNodePath;
use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId;
use Neos\ContentRepository\Core\Projection\ContentGraph\NodePath;

/**
* Node ID -> Node Path cache
Expand All @@ -25,7 +25,7 @@
final class NodePathCache
{
/**
* @var array<string,NodePath>
* @var array<string,AbsoluteNodePath>
*/
protected array $nodePaths = [];

Expand All @@ -36,15 +36,15 @@ public function __construct(bool $isEnabled)
$this->isEnabled = $isEnabled;
}

public function add(NodeAggregateId $nodeAggregateId, NodePath $nodePath): void
public function add(NodeAggregateId $nodeAggregateId, AbsoluteNodePath $nodePath): void
{
if ($this->isEnabled === false) {
return;
}
$this->nodePaths[$nodeAggregateId->value] = $nodePath;
}

public function get(NodeAggregateId $nodeAggregateId): ?NodePath
public function get(NodeAggregateId $nodeAggregateId): ?AbsoluteNodePath
{
if ($this->isEnabled === false) {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,23 +151,28 @@ public function findBackReferences(NodeAggregateId $nodeAggregateId, Filter\Find
public function countBackReferences(NodeAggregateId $nodeAggregateId, Filter\CountBackReferencesFilter $filter): int;

/**
* Find a single node underneath
* - the given root node type name if defined in the path
* - the given starting node aggregate ID else
* that matches the specified $path
* Find a single node underneath $startingNodeAggregateId that matches the specified $path
*
* NOTE: This operation is most likely to be deprecated since the concept of node paths is not really used in the core, and it has some logical issues
* @return Node|null the node that matches the given $path, or NULL if no node on that path is accessible
*/
public function findNodeByPath(NodePath $path, ?NodeAggregateId $startingNodeAggregateId): ?Node;
public function findNodeByPath(NodePath $path, NodeAggregateId $startingNodeAggregateId): ?Node;

/**
* Find a single node underneath that matches the specified absolute $path
*
* NOTE: This operation is most likely to be deprecated since the concept of node paths is not really used in the core, and it has some logical issues
* @return Node|null the node that matches the given $path, or NULL if no node on that path is accessible
*/
public function findNodeByAbsolutePath(AbsoluteNodePath $path): ?Node;

/**
* Determine the absolute path of a node
*
* NOTE: This operation is most likely to be deprecated since the concept of node paths is not really used in the core, and it has some logical issues
* @throws \InvalidArgumentException if the node path could not be retrieved because it is inaccessible or contains no valid path. The latter can happen if any node in the hierarchy has no name
*/
public function retrieveNodePath(NodeAggregateId $nodeAggregateId): NodePath;
public function retrieveNodePath(NodeAggregateId $nodeAggregateId): AbsoluteNodePath;

/**
* Count all nodes in this subgraph, including inaccessible ones!
Expand Down
Loading

0 comments on commit 2b4e5b6

Please sign in to comment.