diff --git a/Neos.ContentGraph.DoctrineDbalAdapter/src/Domain/Repository/ContentGraph.php b/Neos.ContentGraph.DoctrineDbalAdapter/src/Domain/Repository/ContentGraph.php index d21056908c7..54f5799f5ea 100644 --- a/Neos.ContentGraph.DoctrineDbalAdapter/src/Domain/Repository/ContentGraph.php +++ b/Neos.ContentGraph.DoctrineDbalAdapter/src/Domain/Repository/ContentGraph.php @@ -84,6 +84,16 @@ public function __construct( $this->nodeQueryBuilder = new NodeQueryBuilder($this->client->getConnection(), $this->tableNames); } + public function getContentRepositoryId(): ContentRepositoryId + { + return $this->contentRepositoryId; + } + + public function getWorkspaceName(): WorkspaceName + { + return $this->workspaceName; + } + public function getSubgraph( DimensionSpacePoint $dimensionSpacePoint, VisibilityConstraints $visibilityConstraints @@ -171,6 +181,7 @@ public function findNodeAggregateById( return $this->nodeFactory->mapNodeRowsToNodeAggregate( $this->fetchRows($queryBuilder), + $this->workspaceName, $this->contentStreamId, VisibilityConstraints::withoutRestrictions() ); @@ -236,6 +247,7 @@ public function findParentNodeAggregateByChildOriginDimensionSpacePoint(NodeAggr return $this->nodeFactory->mapNodeRowsToNodeAggregate( $this->fetchRows($queryBuilder), + $this->workspaceName, $this->contentStreamId, VisibilityConstraints::withoutRestrictions() ); @@ -304,7 +316,7 @@ public function countNodes(): int try { return (int)$result->fetchOne(); } catch (DriverException | DBALException $e) { - throw new \RuntimeException(sprintf('Failed to fetch rows from database: %s', $e->getMessage()), 1701444590, $e); + throw new \RuntimeException(sprintf('Failed to count rows in database: %s', $e->getMessage()), 1701444590, $e); } } @@ -325,6 +337,7 @@ private function mapQueryBuilderToNodeAggregate(QueryBuilder $queryBuilder): ?No { return $this->nodeFactory->mapNodeRowsToNodeAggregate( $this->fetchRows($queryBuilder), + $this->workspaceName, $this->contentStreamId, VisibilityConstraints::withoutRestrictions() ); @@ -338,6 +351,7 @@ private function mapQueryBuilderToNodeAggregates(QueryBuilder $queryBuilder): it { return $this->nodeFactory->mapNodeRowsToNodeAggregates( $this->fetchRows($queryBuilder), + $this->workspaceName, $this->contentStreamId, VisibilityConstraints::withoutRestrictions() ); @@ -359,13 +373,6 @@ private function fetchRows(QueryBuilder $queryBuilder): array } } - /** The workspace this content graph is operating on */ - public function getWorkspaceName(): WorkspaceName - { - return $this->workspaceName; - } - - /** @internal The content stream id where the workspace name points to for this instance */ public function getContentStreamId(): ContentStreamId { return $this->contentStreamId; diff --git a/Neos.ContentGraph.DoctrineDbalAdapter/src/Domain/Repository/ContentSubgraph.php b/Neos.ContentGraph.DoctrineDbalAdapter/src/Domain/Repository/ContentSubgraph.php index 420b8d2a80f..322ed95e789 100644 --- a/Neos.ContentGraph.DoctrineDbalAdapter/src/Domain/Repository/ContentSubgraph.php +++ b/Neos.ContentGraph.DoctrineDbalAdapter/src/Domain/Repository/ContentSubgraph.php @@ -92,7 +92,6 @@ final class ContentSubgraph implements ContentSubgraphInterface public function __construct( private readonly ContentRepositoryId $contentRepositoryId, - /** @phpstan-ignore-next-line */ private readonly WorkspaceName $workspaceName, private readonly ContentStreamId $contentStreamId, private readonly DimensionSpacePoint $dimensionSpacePoint, @@ -105,14 +104,24 @@ public function __construct( $this->nodeQueryBuilder = new NodeQueryBuilder($this->client->getConnection(), $tableNames); } - public function getIdentity(): ContentSubgraphIdentity + public function getContentRepositoryId(): ContentRepositoryId { - return ContentSubgraphIdentity::create( - $this->contentRepositoryId, - $this->contentStreamId, - $this->dimensionSpacePoint, - $this->visibilityConstraints - ); + return $this->contentRepositoryId; + } + + public function getWorkspaceName(): WorkspaceName + { + return $this->workspaceName; + } + + public function getDimensionSpacePoint(): DimensionSpacePoint + { + return $this->dimensionSpacePoint; + } + + public function getVisibilityConstraints(): VisibilityConstraints + { + return $this->visibilityConstraints; } public function findChildNodes(NodeAggregateId $parentNodeAggregateId, FindChildNodesFilter $filter): Nodes @@ -290,7 +299,13 @@ public function findSubtree(NodeAggregateId $entryNodeAggregateId, FindSubtreeFi foreach (array_reverse($result) as $nodeData) { $nodeAggregateId = $nodeData['nodeaggregateid']; $parentNodeAggregateId = $nodeData['parentNodeAggregateId']; - $node = $this->nodeFactory->mapNodeRowToNode($nodeData, $this->contentStreamId, $this->dimensionSpacePoint, $this->visibilityConstraints); + $node = $this->nodeFactory->mapNodeRowToNode( + $nodeData, + $this->workspaceName, + $this->contentStreamId, + $this->dimensionSpacePoint, + $this->visibilityConstraints + ); $subtree = new Subtree((int)$nodeData['level'], $node, array_key_exists($nodeAggregateId, $subtreesByParentNodeId) ? array_reverse($subtreesByParentNodeId[$nodeAggregateId]) : []); if ($subtree->level === 0) { return $subtree; @@ -319,6 +334,7 @@ public function findAncestorNodes(NodeAggregateId $entryNodeAggregateId, FindAnc return $this->nodeFactory->mapNodeRowsToNodes( $nodeRows, + $this->workspaceName, $this->contentStreamId, $this->dimensionSpacePoint, $this->visibilityConstraints @@ -374,6 +390,7 @@ public function findClosestNode(NodeAggregateId $entryNodeAggregateId, FindClose ); return $this->nodeFactory->mapNodeRowsToNodes( $nodeRows, + $this->workspaceName, $this->contentStreamId, $this->dimensionSpacePoint, $this->visibilityConstraints @@ -391,7 +408,13 @@ public function findDescendantNodes(NodeAggregateId $entryNodeAggregateId, FindD } $queryBuilderCte->addOrderBy('level')->addOrderBy('position'); $nodeRows = $this->fetchCteResults($queryBuilderInitial, $queryBuilderRecursive, $queryBuilderCte, 'tree'); - return $this->nodeFactory->mapNodeRowsToNodes($nodeRows, $this->contentStreamId, $this->dimensionSpacePoint, $this->visibilityConstraints); + return $this->nodeFactory->mapNodeRowsToNodes( + $nodeRows, + $this->workspaceName, + $this->contentStreamId, + $this->dimensionSpacePoint, + $this->visibilityConstraints + ); } public function countDescendantNodes(NodeAggregateId $entryNodeAggregateId, CountDescendantNodesFilter $filter): int @@ -416,9 +439,15 @@ public function countNodes(): int return $result; } - public function jsonSerialize(): ContentSubgraphIdentity + /** + * @return array + */ + public function jsonSerialize(): array { - return $this->getIdentity(); + return [ + 'workspaceName' => $this->workspaceName, + 'dimensionSpacePoint' => $this->dimensionSpacePoint, + ]; } /** ------------------------------------------- */ @@ -668,6 +697,7 @@ private function fetchNode(QueryBuilder $queryBuilder): ?Node } return $this->nodeFactory->mapNodeRowToNode( $nodeRow, + $this->workspaceName, $this->contentStreamId, $this->dimensionSpacePoint, $this->visibilityConstraints @@ -681,7 +711,13 @@ private function fetchNodes(QueryBuilder $queryBuilder): Nodes } catch (DbalDriverException | DbalException $e) { throw new \RuntimeException(sprintf('Failed to fetch nodes: %s', $e->getMessage()), 1678292896, $e); } - return $this->nodeFactory->mapNodeRowsToNodes($nodeRows, $this->contentStreamId, $this->dimensionSpacePoint, $this->visibilityConstraints); + return $this->nodeFactory->mapNodeRowsToNodes( + $nodeRows, + $this->workspaceName, + $this->contentStreamId, + $this->dimensionSpacePoint, + $this->visibilityConstraints + ); } private function fetchCount(QueryBuilder $queryBuilder): int @@ -700,7 +736,7 @@ private function fetchReferences(QueryBuilder $queryBuilder): References } catch (DbalDriverException | DbalException $e) { throw new \RuntimeException(sprintf('Failed to fetch references: %s', $e->getMessage()), 1678364944, $e); } - return $this->nodeFactory->mapReferenceRowsToReferences($referenceRows, $this->contentStreamId, $this->dimensionSpacePoint, $this->visibilityConstraints); + return $this->nodeFactory->mapReferenceRowsToReferences($referenceRows, $this->workspaceName, $this->contentStreamId, $this->dimensionSpacePoint, $this->visibilityConstraints); } /** diff --git a/Neos.ContentGraph.DoctrineDbalAdapter/src/Domain/Repository/NodeFactory.php b/Neos.ContentGraph.DoctrineDbalAdapter/src/Domain/Repository/NodeFactory.php index 88887b67d9a..3e91d0fc05a 100644 --- a/Neos.ContentGraph.DoctrineDbalAdapter/src/Domain/Repository/NodeFactory.php +++ b/Neos.ContentGraph.DoctrineDbalAdapter/src/Domain/Repository/NodeFactory.php @@ -16,7 +16,6 @@ use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePoint; use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePointSet; -use Neos\ContentRepository\Core\DimensionSpace\OriginDimensionSpacePoint; use Neos\ContentRepository\Core\DimensionSpace\OriginDimensionSpacePointSet; use Neos\ContentRepository\Core\Feature\NodeModification\Dto\SerializedPropertyValues; use Neos\ContentRepository\Core\Feature\SubtreeTagging\Dto\SubtreeTags; @@ -38,11 +37,13 @@ use Neos\ContentRepository\Core\Projection\ContentGraph\VisibilityConstraints; use Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId; use Neos\ContentRepository\Core\SharedModel\Exception\NodeTypeNotFoundException; +use Neos\ContentRepository\Core\SharedModel\Node\NodeAddress; 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\Node\ReferenceName; use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId; +use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; /** * Implementation detail of ContentGraph and ContentSubgraph @@ -64,6 +65,7 @@ public function __construct( */ public function mapNodeRowToNode( array $nodeRow, + WorkspaceName $workspaceName, ContentStreamId $contentStreamId, DimensionSpacePoint $dimensionSpacePoint, VisibilityConstraints $visibilityConstraints @@ -73,17 +75,13 @@ public function mapNodeRowToNode( : null; return Node::create( - ContentSubgraphIdentity::create( - $this->contentRepositoryId, - $contentStreamId, - $dimensionSpacePoint, - $visibilityConstraints - ), + $this->contentRepositoryId, + $workspaceName, + $dimensionSpacePoint, NodeAggregateId::fromString($nodeRow['nodeaggregateid']), $this->dimensionSpacePointRepository->getOriginDimensionSpacePointByHash($nodeRow['origindimensionspacepointhash']), NodeAggregateClassification::from($nodeRow['classification']), NodeTypeName::fromString($nodeRow['nodetypename']), - $nodeType, $this->createPropertyCollectionFromJsonString($nodeRow['properties']), isset($nodeRow['name']) ? NodeName::fromString($nodeRow['name']) : null, self::extractNodeTagsFromJson($nodeRow['subtreetags']), @@ -93,16 +91,30 @@ public function mapNodeRowToNode( isset($nodeRow['lastmodified']) ? self::parseDateTimeString($nodeRow['lastmodified']) : null, isset($nodeRow['originallastmodified']) ? self::parseDateTimeString($nodeRow['originallastmodified']) : null, ), + $visibilityConstraints, + $nodeType, + $contentStreamId ); } /** * @param array> $nodeRows */ - public function mapNodeRowsToNodes(array $nodeRows, ContentStreamId $contentStreamId, DimensionSpacePoint $dimensionSpacePoint, VisibilityConstraints $visibilityConstraints): Nodes - { + public function mapNodeRowsToNodes( + array $nodeRows, + WorkspaceName $workspaceName, + ContentStreamId $contentStreamId, + DimensionSpacePoint $dimensionSpacePoint, + VisibilityConstraints $visibilityConstraints + ): Nodes { return Nodes::fromArray( - array_map(fn (array $nodeRow) => $this->mapNodeRowToNode($nodeRow, $contentStreamId, $dimensionSpacePoint, $visibilityConstraints), $nodeRows) + array_map(fn (array $nodeRow) => $this->mapNodeRowToNode( + $nodeRow, + $workspaceName, + $contentStreamId, + $dimensionSpacePoint, + $visibilityConstraints + ), $nodeRows) ); } @@ -119,6 +131,7 @@ public function createPropertyCollectionFromJsonString(string $jsonString): Prop */ public function mapReferenceRowsToReferences( array $nodeRows, + WorkspaceName $workspaceName, ContentStreamId $contentStreamId, DimensionSpacePoint $dimensionSpacePoint, VisibilityConstraints $visibilityConstraints @@ -127,6 +140,7 @@ public function mapReferenceRowsToReferences( foreach ($nodeRows as $nodeRow) { $node = $this->mapNodeRowToNode( $nodeRow, + $workspaceName, $contentStreamId, $dimensionSpacePoint, $visibilityConstraints @@ -149,6 +163,7 @@ public function mapReferenceRowsToReferences( */ public function mapNodeRowsToNodeAggregate( array $nodeRows, + WorkspaceName $workspaceName, ContentStreamId $contentStreamId, VisibilityConstraints $visibilityConstraints ): ?NodeAggregate { @@ -175,6 +190,7 @@ public function mapNodeRowsToNodeAggregate( // ... so we handle occupation exactly once ... $nodesByOccupiedDimensionSpacePoints[$occupiedDimensionSpacePoint->hash] = $this->mapNodeRowToNode( $nodeRow, + $workspaceName, $contentStreamId, $occupiedDimensionSpacePoint->toDimensionSpacePoint(), $visibilityConstraints @@ -230,6 +246,7 @@ public function mapNodeRowsToNodeAggregate( */ public function mapNodeRowsToNodeAggregates( iterable $nodeRows, + WorkspaceName $workspaceName, ContentStreamId $contentStreamId, VisibilityConstraints $visibilityConstraints ): iterable { @@ -256,6 +273,7 @@ public function mapNodeRowsToNodeAggregates( $nodesByOccupiedDimensionSpacePointsByNodeAggregate [$rawNodeAggregateId][$occupiedDimensionSpacePoint->hash] = $this->mapNodeRowToNode( $nodeRow, + $workspaceName, $contentStreamId, $occupiedDimensionSpacePoint->toDimensionSpacePoint(), $visibilityConstraints diff --git a/Neos.ContentGraph.PostgreSQLAdapter/src/Domain/Repository/ContentHypergraph.php b/Neos.ContentGraph.PostgreSQLAdapter/src/Domain/Repository/ContentHypergraph.php index 678b3d69adc..8ed8bc21557 100644 --- a/Neos.ContentGraph.PostgreSQLAdapter/src/Domain/Repository/ContentHypergraph.php +++ b/Neos.ContentGraph.PostgreSQLAdapter/src/Domain/Repository/ContentHypergraph.php @@ -68,6 +68,16 @@ public function __construct( $this->nodeFactory = $nodeFactory; } + public function getContentRepositoryId(): ContentRepositoryId + { + return $this->contentRepositoryId; + } + + public function getWorkspaceName(): WorkspaceName + { + return $this->workspaceName; + } + public function getSubgraph( DimensionSpacePoint $dimensionSpacePoint, VisibilityConstraints $visibilityConstraints @@ -77,6 +87,7 @@ public function getSubgraph( $this->subhypergraphs[$index] = new ContentSubhypergraph( $this->contentRepositoryId, $this->contentStreamId, + $this->workspaceName, $dimensionSpacePoint, $visibilityConstraints, $this->databaseClient, @@ -307,11 +318,6 @@ private function getDatabaseConnection(): DatabaseConnection return $this->databaseClient->getConnection(); } - public function getWorkspaceName(): WorkspaceName - { - return $this->workspaceName; - } - public function getContentStreamId(): ContentStreamId { return $this->contentStreamId; diff --git a/Neos.ContentGraph.PostgreSQLAdapter/src/Domain/Repository/ContentSubhypergraph.php b/Neos.ContentGraph.PostgreSQLAdapter/src/Domain/Repository/ContentSubhypergraph.php index 95d96cbf077..b25c7452294 100644 --- a/Neos.ContentGraph.PostgreSQLAdapter/src/Domain/Repository/ContentSubhypergraph.php +++ b/Neos.ContentGraph.PostgreSQLAdapter/src/Domain/Repository/ContentSubhypergraph.php @@ -52,6 +52,7 @@ use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId; use Neos\ContentRepository\Core\SharedModel\Node\NodeName; use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId; +use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; /** * The content subgraph application repository @@ -76,6 +77,7 @@ public function __construct( private ContentRepositoryId $contentRepositoryId, private ContentStreamId $contentStreamId, + private WorkspaceName $workspaceName, private DimensionSpacePoint $dimensionSpacePoint, private VisibilityConstraints $visibilityConstraints, private PostgresDbalClientInterface $databaseClient, @@ -85,14 +87,24 @@ public function __construct( ) { } - public function getIdentity(): ContentSubgraphIdentity + public function getContentRepositoryId(): ContentRepositoryId { - return ContentSubgraphIdentity::create( - $this->contentRepositoryId, - $this->contentStreamId, - $this->dimensionSpacePoint, - $this->visibilityConstraints - ); + return $this->contentRepositoryId; + } + + public function getWorkspaceName(): WorkspaceName + { + return $this->workspaceName; + } + + public function getDimensionSpacePoint(): DimensionSpacePoint + { + return $this->dimensionSpacePoint; + } + + public function getVisibilityConstraints(): VisibilityConstraints + { + return $this->visibilityConstraints; } public function findNodeById(NodeAggregateId $nodeAggregateId): ?Node @@ -534,8 +546,14 @@ private function getDatabaseConnection(): DatabaseConnection return $this->databaseClient->getConnection(); } - public function jsonSerialize(): ContentSubgraphIdentity + /** + * @return array + */ + public function jsonSerialize(): array { - return $this->getIdentity(); + return [ + 'workspaceName' => $this->workspaceName, + 'dimensionSpacePoint' => $this->dimensionSpacePoint, + ]; } } diff --git a/Neos.ContentGraph.PostgreSQLAdapter/src/Domain/Repository/NodeFactory.php b/Neos.ContentGraph.PostgreSQLAdapter/src/Domain/Repository/NodeFactory.php index da9b96cf28c..dbd29ff6642 100644 --- a/Neos.ContentGraph.PostgreSQLAdapter/src/Domain/Repository/NodeFactory.php +++ b/Neos.ContentGraph.PostgreSQLAdapter/src/Domain/Repository/NodeFactory.php @@ -37,11 +37,13 @@ use Neos\ContentRepository\Core\Projection\ContentGraph\Timestamps; use Neos\ContentRepository\Core\Projection\ContentGraph\VisibilityConstraints; use Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId; +use Neos\ContentRepository\Core\SharedModel\Node\NodeAddress; 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\Node\ReferenceName; use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId; +use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; /** * The node factory for mapping database rows to nodes and node aggregates @@ -77,17 +79,14 @@ public function mapNodeRowToNode( : null; return Node::create( - ContentSubgraphIdentity::create( - $this->contentRepositoryId, - $contentStreamId ?: ContentStreamId::fromString($nodeRow['contentstreamid']), - $dimensionSpacePoint ?: DimensionSpacePoint::fromJsonString($nodeRow['dimensionspacepoint']), - $visibilityConstraints - ), + $this->contentRepositoryId, + // todo use actual workspace name + WorkspaceName::fromString('missing'), + $dimensionSpacePoint ?: DimensionSpacePoint::fromJsonString($nodeRow['dimensionspacepoint']), NodeAggregateId::fromString($nodeRow['nodeaggregateid']), OriginDimensionSpacePoint::fromJsonString($nodeRow['origindimensionspacepoint']), NodeAggregateClassification::from($nodeRow['classification']), NodeTypeName::fromString($nodeRow['nodetypename']), - $nodeType, new PropertyCollection( SerializedPropertyValues::fromJsonString($nodeRow['properties']), $this->propertyConverter @@ -102,6 +101,9 @@ public function mapNodeRowToNode( isset($nodeRow['lastmodified']) ? self::parseDateTimeString($nodeRow['lastmodified']) : null, isset($nodeRow['originallastmodified']) ? self::parseDateTimeString($nodeRow['originallastmodified']) : null, ), + $visibilityConstraints, + $nodeType, + $contentStreamId ?: ContentStreamId::fromString($nodeRow['contentstreamid']), ); } diff --git a/Neos.ContentRepository.Core/Classes/DimensionSpace/DimensionSpacePoint.php b/Neos.ContentRepository.Core/Classes/DimensionSpace/DimensionSpacePoint.php index 9a31aef29ad..31532f5e148 100644 --- a/Neos.ContentRepository.Core/Classes/DimensionSpace/DimensionSpacePoint.php +++ b/Neos.ContentRepository.Core/Classes/DimensionSpace/DimensionSpacePoint.php @@ -19,8 +19,6 @@ /** * A point in the dimension space with coordinates DimensionName => DimensionValue. * E.g.: ["language" => "es", "country" => "ar"] - * - * Implements CacheAwareInterface because of Fusion Runtime caching and Routing * @api */ final class DimensionSpacePoint extends AbstractDimensionSpacePoint diff --git a/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/ContentGraphInterface.php b/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/ContentGraphInterface.php index 4913da031cf..6a5ef9a7948 100644 --- a/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/ContentGraphInterface.php +++ b/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/ContentGraphInterface.php @@ -19,6 +19,7 @@ use Neos\ContentRepository\Core\DimensionSpace\OriginDimensionSpacePoint; use Neos\ContentRepository\Core\NodeType\NodeTypeName; use Neos\ContentRepository\Core\Projection\ProjectionStateInterface; +use Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId; use Neos\ContentRepository\Core\SharedModel\Exception\NodeAggregatesTypeIsAmbiguous; use Neos\ContentRepository\Core\SharedModel\Exception\RootNodeAggregateDoesNotExist; use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId; @@ -37,6 +38,17 @@ */ interface ContentGraphInterface extends ProjectionStateInterface { + /** + * @api + */ + public function getContentRepositoryId(): ContentRepositoryId; + + /** + * The workspace this content graph is operating on + * @api + */ + public function getWorkspaceName(): WorkspaceName; + /** * @api main API method of ContentGraph */ @@ -149,9 +161,6 @@ public function getDimensionSpacePointsOccupiedByChildNodeName( */ public function countNodes(): int; - /** The workspace this content graph is operating on */ - public function getWorkspaceName(): WorkspaceName; - /** @internal The content stream id where the workspace name points to for this instance */ public function getContentStreamId(): ContentStreamId; } diff --git a/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/ContentGraphWithRuntimeCaches/ContentSubgraphWithRuntimeCaches.php b/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/ContentGraphWithRuntimeCaches/ContentSubgraphWithRuntimeCaches.php index 96ec21720d7..72510b43e62 100644 --- a/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/ContentGraphWithRuntimeCaches/ContentSubgraphWithRuntimeCaches.php +++ b/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/ContentGraphWithRuntimeCaches/ContentSubgraphWithRuntimeCaches.php @@ -14,9 +14,9 @@ namespace Neos\ContentRepository\Core\Projection\ContentGraph\ContentGraphWithRuntimeCaches; +use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePoint; use Neos\ContentRepository\Core\NodeType\NodeTypeName; use Neos\ContentRepository\Core\Projection\ContentGraph\AbsoluteNodePath; -use Neos\ContentRepository\Core\Projection\ContentGraph\ContentSubgraphIdentity; use Neos\ContentRepository\Core\Projection\ContentGraph\ContentSubgraphInterface; use Neos\ContentRepository\Core\Projection\ContentGraph\Filter; use Neos\ContentRepository\Core\Projection\ContentGraph\Filter\CountBackReferencesFilter; @@ -34,8 +34,11 @@ use Neos\ContentRepository\Core\Projection\ContentGraph\Nodes; use Neos\ContentRepository\Core\Projection\ContentGraph\References; use Neos\ContentRepository\Core\Projection\ContentGraph\Subtree; +use Neos\ContentRepository\Core\Projection\ContentGraph\VisibilityConstraints; +use Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId; use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId; use Neos\ContentRepository\Core\SharedModel\Node\NodeName; +use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; /** * Wrapper for a concrete implementation of the {@see ContentSubgraphInterface} that @@ -53,9 +56,24 @@ public function __construct( $this->inMemoryCache = new InMemoryCache(); } - public function getIdentity(): ContentSubgraphIdentity + public function getContentRepositoryId(): ContentRepositoryId { - return $this->wrappedContentSubgraph->getIdentity(); + return $this->wrappedContentSubgraph->getContentRepositoryId(); + } + + public function getWorkspaceName(): WorkspaceName + { + return $this->wrappedContentSubgraph->getWorkspaceName(); + } + + public function getDimensionSpacePoint(): DimensionSpacePoint + { + return $this->wrappedContentSubgraph->getDimensionSpacePoint(); + } + + public function getVisibilityConstraints(): VisibilityConstraints + { + return $this->wrappedContentSubgraph->getVisibilityConstraints(); } public function findChildNodes(NodeAggregateId $parentNodeAggregateId, FindChildNodesFilter $filter): Nodes @@ -247,7 +265,10 @@ private static function isFilterEmpty(object $filter): bool return array_filter(get_object_vars($filter), static fn ($value) => $value !== null) === []; } - public function jsonSerialize(): ContentSubgraphIdentity + /** + * @return array + */ + public function jsonSerialize(): array { return $this->wrappedContentSubgraph->jsonSerialize(); } diff --git a/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/ContentSubgraphIdentity.php b/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/ContentSubgraphIdentity.php index 40b35d78116..8c00f8e1268 100644 --- a/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/ContentSubgraphIdentity.php +++ b/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/ContentSubgraphIdentity.php @@ -4,6 +4,7 @@ use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePoint; use Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId; +use Neos\ContentRepository\Core\SharedModel\Node\NodeAddress; use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId; /** @@ -31,7 +32,8 @@ * usually need this method instead of the Origin DimensionSpacePoint inside the read model; and you'll * need the OriginDimensionSpacePoint when constructing commands on the write side. * - * @api + * @deprecated please use the {@see NodeAddress} instead {@see Node::$subgraphIdentity}. Will be removed before the Final Neos 9 release. + * @internal */ final readonly class ContentSubgraphIdentity implements \JsonSerializable { @@ -46,9 +48,6 @@ private function __construct( ) { } - /** - * @api - */ public static function create( ContentRepositoryId $contentRepositoryId, ContentStreamId $contentStreamId, diff --git a/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/ContentSubgraphInterface.php b/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/ContentSubgraphInterface.php index 79a1427ec93..083e85cdcf2 100644 --- a/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/ContentSubgraphInterface.php +++ b/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/ContentSubgraphInterface.php @@ -17,9 +17,10 @@ use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePoint; use Neos\ContentRepository\Core\Feature\RootNodeCreation\RootNodeHandling; use Neos\ContentRepository\Core\NodeType\NodeTypeName; +use Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId; use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId; use Neos\ContentRepository\Core\SharedModel\Node\NodeName; -use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId; +use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; /** * This is the most important read model of a content repository. @@ -48,14 +49,13 @@ */ interface ContentSubgraphInterface extends \JsonSerializable { - /** - * Returns the subgraph's identity, i.e. the current perspective we look at content from, composed of - * * the content repository the subgraph belongs to - * * the ID of the content stream we are currently working in - * * the dimension space point we are currently looking at - * * the applied visibility constraints - */ - public function getIdentity(): ContentSubgraphIdentity; + public function getContentRepositoryId(): ContentRepositoryId; + + public function getWorkspaceName(): WorkspaceName; + + public function getDimensionSpacePoint(): DimensionSpacePoint; + + public function getVisibilityConstraints(): VisibilityConstraints; /** * Find a single node by its aggregate id @@ -204,5 +204,9 @@ public function retrieveNodePath(NodeAggregateId $nodeAggregateId): AbsoluteNode */ public function countNodes(): int; - public function jsonSerialize(): ContentSubgraphIdentity; + /** + * @deprecated will be removed before Neos 9 release + * @return array + */ + public function jsonSerialize(): array; } diff --git a/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/Node.php b/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/Node.php index 48250a8107c..70a79fc5fa8 100644 --- a/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/Node.php +++ b/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/Node.php @@ -14,12 +14,17 @@ namespace Neos\ContentRepository\Core\Projection\ContentGraph; +use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePoint; use Neos\ContentRepository\Core\DimensionSpace\OriginDimensionSpacePoint; use Neos\ContentRepository\Core\NodeType\NodeType; use Neos\ContentRepository\Core\NodeType\NodeTypeName; +use Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId; +use Neos\ContentRepository\Core\SharedModel\Node\NodeAddress; 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; +use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; /** * Main read model of the {@see ContentSubgraphInterface}. @@ -27,52 +32,138 @@ * Immutable, Read Only. In case you want to modify it, you need * to create Commands and send them to ContentRepository::handle. * + * ## Identity of a Node + * + * The node's "Read Model" identity is summarized here {@see NodeAddress}, consisting of: + * + * - {@see ContentRepositoryId} + * - {@see WorkspaceName} + * - {@see DimensionSpacePoint} + * - {@see NodeAggregateId} + * + * The node address can be constructed via {@see NodeAddress::fromNode()} and serialized. + * + * ## Traversing the graph + * * The node does not have structure information, i.e. no infos * about its children. To f.e. fetch children, you need to fetch - * the subgraph {@see ContentGraphInterface::getSubgraph()} via - * $subgraphIdentity {@see Node::$subgraphIdentity}. and then - * call findChildNodes() {@see ContentSubgraphInterface::findChildNodes()} - * on the subgraph. + * the subgraph and use findChildNodes on the subgraph: + * + * $subgraph = $contentRepository->getContentGraph($node->workspaceName)->getSubgraph( + * $node->dimensionSpacePoint, + * $node->visibilityConstraints + * ); + * $childNodes = $subgraph->findChildNodes($node->aggregateId, FindChildNodesFilter::create()); + * + * ## A note about the {@see DimensionSpacePoint} and the {@see OriginDimensionSpacePoint} + * + * The {@see Node::dimensionSpacePoint} is the DimensionSpacePoint this node has been accessed in, + * and NOT the DimensionSpacePoint where the node is "at home". + * The DimensionSpacePoint where the node is (at home) is called the ORIGIN DimensionSpacePoint, + * and this can be accessed using {@see Node::originDimensionSpacePoint}. If in doubt, you'll + * usually need the DimensionSpacePoint instead of the OriginDimensionSpacePoint; + * you'll only need the OriginDimensionSpacePoint when constructing commands on the write side. * * @api Note: The constructor is not part of the public API */ final readonly class Node { /** - * @param ContentSubgraphIdentity $subgraphIdentity This is part of the node's "Read Model" identity which is defined by: {@see self::subgraphIdentity} and {@see self::nodeAggregateId}. With this information, you can fetch a Subgraph using {@see ContentGraphInterface::getSubgraph()}. - * @param NodeAggregateId $nodeAggregateId NodeAggregateId (identifier) of this node. This is part of the node's "Read Model" identity which is defined by: {@see self::subgraphIdentity} and {@see self::nodeAggregateId} + * This was intermediate part of the node's "Read Model" identity. + * Please use {@see $contentRepositoryId} {@see $workspaceName} {@see $dimensionSpacePoint} and {@see $aggregateId} instead, + * or see {@see NodeAddress::fromNode()} for passing the identity around. + * The visibility-constraints now reside in {@see $visibilityConstraints}. + * There is no replacement for the previously attached content-stream-id. Please refactor the code to use the newly available workspace-name. + * @deprecated will be removed before the final 9.0 release + */ + public ContentSubgraphIdentity $subgraphIdentity; + + /** + * In PHP please use {@see $aggregateId} instead. + * + * For Fusion please use the upcoming FlowQuery operation: + * ``` + * ${q(node).id()} + * ``` + * @deprecated will be removed before the final 9.0 release + */ + public NodeAggregateId $nodeAggregateId; + + /** + * In PHP please use {@see $name} instead. + * + * For Fusion use: + * ``` + * ${node.name.value} + * ``` + * @deprecated will be removed before the final 9.0 release + */ + public ?NodeName $nodeName; + + /** + * In PHP please fetch the NodeType via the NodeTypeManager or the NodeTypeWithFallbackProvider trait instead. + * {@see $nodeTypeName} + * + * For Fusion please use the EEL Helper: + * ``` + * ${Neos.Node.getNodeType(node)} + * ``` + * @deprecated will be removed before the final 9.0 release + */ + public ?NodeType $nodeType; + + /** + * @param ContentRepositoryId $contentRepositoryId The content-repository this Node belongs to + * @param WorkspaceName $workspaceName The workspace of this Node + * @param DimensionSpacePoint $dimensionSpacePoint DimensionSpacePoint a node has been accessed in + * @param NodeAggregateId $aggregateId NodeAggregateId (identifier) of this node. This is part of the node's "Read Model" identity, which is defined in {@see NodeAddress} * @param OriginDimensionSpacePoint $originDimensionSpacePoint The DimensionSpacePoint the node originates in. Usually needed to address a Node in a NodeAggregate in order to update it. * @param NodeAggregateClassification $classification The classification (regular, root, tethered) of this node * @param NodeTypeName $nodeTypeName The node's node type name; always set, even if unknown to the NodeTypeManager * @param NodeType|null $nodeType The node's node type, null if unknown to the NodeTypeManager - @deprecated Don't rely on this too much, as the capabilities of the NodeType here will probably change a lot; Ask the {@see NodeTypeManager} instead * @param PropertyCollection $properties All properties of this node. References are NOT part of this API; To access references, {@see ContentSubgraphInterface::findReferences()} can be used; To read the serialized properties use {@see PropertyCollection::serialized()}. - * @param NodeName|null $nodeName The optional name of the node, describing its relation to its parent + * @param NodeName|null $name The optional name of the node, describing its relation to its parent * @param NodeTags $tags explicit and inherited SubtreeTags of this node * @param Timestamps $timestamps Creation and modification timestamps of this node + * @param VisibilityConstraints $visibilityConstraints Information which subgraph filter was used to access this node */ private function __construct( - public ContentSubgraphIdentity $subgraphIdentity, - public NodeAggregateId $nodeAggregateId, + public ContentRepositoryId $contentRepositoryId, + public WorkspaceName $workspaceName, + public DimensionSpacePoint $dimensionSpacePoint, + public NodeAggregateId $aggregateId, public OriginDimensionSpacePoint $originDimensionSpacePoint, public NodeAggregateClassification $classification, public NodeTypeName $nodeTypeName, - public ?NodeType $nodeType, public PropertyCollection $properties, - public ?NodeName $nodeName, + public ?NodeName $name, public NodeTags $tags, public Timestamps $timestamps, + public VisibilityConstraints $visibilityConstraints, + ?NodeType $nodeType, + ContentStreamId $contentStreamId ) { - if ($this->classification->isTethered() && $this->nodeName === null) { + if ($this->classification->isTethered() && $this->name === null) { throw new \InvalidArgumentException('The NodeName must be set if the Node is tethered.', 1695118377); } + // legacy to be removed before Neos9 + $this->nodeAggregateId = $this->aggregateId; + $this->nodeName = $this->name; + $this->nodeType = $nodeType; + $this->subgraphIdentity = ContentSubgraphIdentity::create( + $contentRepositoryId, + $contentStreamId, + $dimensionSpacePoint, + $visibilityConstraints + ); } /** * @internal The signature of this method can change in the future! */ - public static function create(ContentSubgraphIdentity $subgraphIdentity, NodeAggregateId $nodeAggregateId, OriginDimensionSpacePoint $originDimensionSpacePoint, NodeAggregateClassification $classification, NodeTypeName $nodeTypeName, ?NodeType $nodeType, PropertyCollection $properties, ?NodeName $nodeName, NodeTags $tags, Timestamps $timestamps): self + public static function create(ContentRepositoryId $contentRepositoryId, WorkspaceName $workspaceName, DimensionSpacePoint $dimensionSpacePoint, NodeAggregateId $aggregateId, OriginDimensionSpacePoint $originDimensionSpacePoint, NodeAggregateClassification $classification, NodeTypeName $nodeTypeName, PropertyCollection $properties, ?NodeName $nodeName, NodeTags $tags, Timestamps $timestamps, VisibilityConstraints $visibilityConstraints, ?NodeType $nodeType, ContentStreamId $contentStreamId): self { - return new self($subgraphIdentity, $nodeAggregateId, $originDimensionSpacePoint, $classification, $nodeTypeName, $nodeType, $properties, $nodeName, $tags, $timestamps); + return new self($contentRepositoryId, $workspaceName, $dimensionSpacePoint, $aggregateId, $originDimensionSpacePoint, $classification, $nodeTypeName, $properties, $nodeName, $tags, $timestamps, $visibilityConstraints, $nodeType, $contentStreamId); } /** @@ -111,9 +202,14 @@ public function getLabel(): string return $this->nodeType?->getNodeLabelGenerator()->getLabel($this) ?: $this->nodeTypeName->value; } + /** + * Checks if the node's "Read Model" identity equals with the given one + */ public function equals(Node $other): bool { - return $this->subgraphIdentity->equals($other->subgraphIdentity) - && $this->nodeAggregateId->equals($other->nodeAggregateId); + return $this->contentRepositoryId->equals($other->contentRepositoryId) + && $this->workspaceName->equals($other->workspaceName) + && $this->dimensionSpacePoint->equals($other->dimensionSpacePoint) + && $this->aggregateId->equals($other->aggregateId); } } diff --git a/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/NodeAggregate.php b/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/NodeAggregate.php index 15fa8537905..d75692f9369 100644 --- a/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/NodeAggregate.php +++ b/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/NodeAggregate.php @@ -65,6 +65,7 @@ * @param OriginByCoverage $occupationByCovered * @param DimensionSpacePointsBySubtreeTags $dimensionSpacePointsBySubtreeTags dimension space points for every subtree tag this aggregate is *explicitly* tagged with (excluding inherited tags) */ + // todo add workspace name and content repository id and remove cs id public function __construct( public ContentStreamId $contentStreamId, public NodeAggregateId $nodeAggregateId, diff --git a/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/VisibilityConstraints.php b/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/VisibilityConstraints.php index b3d6f466c7e..7462e28a4e1 100644 --- a/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/VisibilityConstraints.php +++ b/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/VisibilityConstraints.php @@ -39,6 +39,10 @@ public function getHash(): string return md5(implode('|', $this->tagConstraints->toStringArray())); } + /** + * A non restricted subgraph can find all nodes without filtering. + * Disabled nodes are this way also findable. + */ public static function withoutRestrictions(): self { return new self(SubtreeTags::createEmpty()); diff --git a/Neos.ContentRepository.Core/Classes/SharedModel/Node/NodeAddress.php b/Neos.ContentRepository.Core/Classes/SharedModel/Node/NodeAddress.php new file mode 100644 index 00000000000..d085a58686c --- /dev/null +++ b/Neos.ContentRepository.Core/Classes/SharedModel/Node/NodeAddress.php @@ -0,0 +1,106 @@ +getContentGraph($nodeAddress->workspaceName)->getSubgraph( + * $nodeAddress->dimensionSpacePoint, + * VisibilityConstraints::withoutRestrictions() + * ); + * $node = $subgraph->findNodeById($nodeAddress->aggregateId); + * + * @api + */ +final readonly class NodeAddress implements \JsonSerializable +{ + private function __construct( + public ContentRepositoryId $contentRepositoryId, + public WorkspaceName $workspaceName, + public DimensionSpacePoint $dimensionSpacePoint, + public NodeAggregateId $aggregateId, + ) { + } + + public static function create( + ContentRepositoryId $contentRepositoryId, + WorkspaceName $workspaceName, + DimensionSpacePoint $dimensionSpacePoint, + NodeAggregateId $aggregateId, + ): self { + return new self($contentRepositoryId, $workspaceName, $dimensionSpacePoint, $aggregateId); + } + + public static function fromNode(Node $node): self + { + return new self( + $node->contentRepositoryId, + $node->workspaceName, + $node->dimensionSpacePoint, + $node->nodeAggregateId + ); + } + + /** + * @param array $array + */ + public static function fromArray(array $array): self + { + return new self( + ContentRepositoryId::fromString($array['contentRepositoryId']), + WorkspaceName::fromString($array['workspaceName']), + DimensionSpacePoint::fromArray($array['dimensionSpacePoint']), + NodeAggregateId::fromString($array['aggregateId']) + ); + } + + public static function fromJsonString(string $jsonString): self + { + return self::fromArray(\json_decode($jsonString, true, JSON_THROW_ON_ERROR)); + } + + public function withAggregateId(NodeAggregateId $aggregateId): self + { + return new self($this->contentRepositoryId, $this->workspaceName, $this->dimensionSpacePoint, $aggregateId); + } + + public function equals(self $other): bool + { + return $this->contentRepositoryId->equals($other->contentRepositoryId) + && $this->workspaceName->equals($other->workspaceName) + && $this->dimensionSpacePoint->equals($other->dimensionSpacePoint) + && $this->aggregateId->equals($other->aggregateId); + } + + public function toJson(): string + { + try { + return json_encode($this, JSON_THROW_ON_ERROR); + } catch (\JsonException $e) { + throw new \RuntimeException(sprintf('Failed to JSON-encode NodeAddress: %s', $e->getMessage()), 1715608338, $e); + } + } + + public function jsonSerialize(): mixed + { + return get_object_vars($this); + } +} diff --git a/Neos.ContentRepository.NodeAccess/Classes/FlowQueryOperations/CreateNodeHashTrait.php b/Neos.ContentRepository.NodeAccess/Classes/FlowQueryOperations/CreateNodeHashTrait.php index 3e7e8b821f4..8475f3ddcbd 100644 --- a/Neos.ContentRepository.NodeAccess/Classes/FlowQueryOperations/CreateNodeHashTrait.php +++ b/Neos.ContentRepository.NodeAccess/Classes/FlowQueryOperations/CreateNodeHashTrait.php @@ -9,8 +9,8 @@ trait CreateNodeHashTrait { /** - * Create a string hash containing the nodeAggregateId, cr-id, contentStream->id, dimensionSpacePoint->hash - * and visibilityConstraints->hash. To be used for ensuring uniqueness or removing nodes. + * Create a string hash containing the node-aggregateId, cr-id, workspace-name, dimensionSpacePoint-hash + * and visibilityConstraints-hash. To be used for ensuring uniqueness or removing nodes. * * @see Node::equals() for comparison */ @@ -21,10 +21,10 @@ protected function createNodeHash(Node $node): string ':', [ $node->nodeAggregateId->value, - $node->subgraphIdentity->contentRepositoryId->value, - $node->subgraphIdentity->contentStreamId->value, - $node->subgraphIdentity->dimensionSpacePoint->hash, - $node->subgraphIdentity->visibilityConstraints->getHash() + $node->contentRepositoryId->value, + $node->workspaceName->value, + $node->dimensionSpacePoint->hash, + $node->visibilityConstraints->getHash() ] ) ); diff --git a/Neos.ContentRepository.NodeAccess/Classes/FlowQueryOperations/FindOperation.php b/Neos.ContentRepository.NodeAccess/Classes/FlowQueryOperations/FindOperation.php index ba8aa21e5ba..c3f5908b6e1 100644 --- a/Neos.ContentRepository.NodeAccess/Classes/FlowQueryOperations/FindOperation.php +++ b/Neos.ContentRepository.NodeAccess/Classes/FlowQueryOperations/FindOperation.php @@ -62,6 +62,8 @@ */ class FindOperation extends AbstractOperation { + use CreateNodeHashTrait; + /** * {@inheritdoc} * @@ -179,7 +181,7 @@ public function evaluate(FlowQuery $flowQuery, array $arguments): void $uniqueResult = []; $usedKeys = []; foreach ($result as $item) { - $identifier = $item->subgraphIdentity->contentStreamId->value . '@' . $item->subgraphIdentity->dimensionSpacePoint->hash . '@' . $item->nodeAggregateId->value; + $identifier = $this->createNodeHash($item); if (!isset($usedKeys[$identifier])) { $uniqueResult[] = $item; $usedKeys[$identifier] = $identifier; @@ -199,8 +201,8 @@ protected function getEntryPoints(array $contextNodes): array foreach ($contextNodes as $contextNode) { assert($contextNode instanceof Node); $subgraph = $this->contentRepositoryRegistry->subgraphForNode($contextNode); - $subgraphIdentifier = md5($contextNode->subgraphIdentity->contentStreamId->value - . '@' . $contextNode->subgraphIdentity->dimensionSpacePoint->toJson()); + $subgraphIdentifier = md5($subgraph->getWorkspaceName()->value + . '@' . $subgraph->getDimensionSpacePoint()->toJson()); if (!isset($entryPoints[(string) $subgraphIdentifier])) { $entryPoints[(string) $subgraphIdentifier] = [ 'subgraph' => $subgraph, diff --git a/Neos.ContentRepository.StructureAdjustment/src/Adjustment/TetheredNodeAdjustments.php b/Neos.ContentRepository.StructureAdjustment/src/Adjustment/TetheredNodeAdjustments.php index e80974464f0..73195ff0991 100644 --- a/Neos.ContentRepository.StructureAdjustment/src/Adjustment/TetheredNodeAdjustments.php +++ b/Neos.ContentRepository.StructureAdjustment/src/Adjustment/TetheredNodeAdjustments.php @@ -34,7 +34,6 @@ class TetheredNodeAdjustments use TetheredNodeInternals; public function __construct( - private readonly ContentRepository $contentRepository, private readonly ProjectedNodeIterator $projectedNodeIterator, private readonly NodeTypeManager $nodeTypeManager, private readonly DimensionSpace\InterDimensionalVariationGraph $interDimensionalVariationGraph, @@ -55,8 +54,6 @@ public function findAdjustmentsForNodeType(NodeTypeName $nodeTypeName): \Generat $expectedTetheredNodes = $this->nodeTypeManager->getTetheredNodesConfigurationForNodeType($nodeType); foreach ($this->projectedNodeIterator->nodeAggregatesOfType($nodeTypeName) as $nodeAggregate) { - // TODO: We should use $nodeAggregate->workspaceName as soon as it's available - $contentGraph = $this->contentRepository->getContentGraph(WorkspaceName::forLive()); // find missing tethered nodes $foundMissingOrDisallowedTetheredNodes = false; $originDimensionSpacePoints = $nodeType->isOfType(NodeTypeName::ROOT_NODE_TYPE_NAME) @@ -69,7 +66,7 @@ public function findAdjustmentsForNodeType(NodeTypeName $nodeTypeName): \Generat foreach ($expectedTetheredNodes as $tetheredNodeName => $expectedTetheredNodeType) { $tetheredNodeName = NodeName::fromString($tetheredNodeName); - $tetheredNode = $contentGraph->getSubgraph( + $tetheredNode = $this->projectedNodeIterator->contentGraph->getSubgraph( $originDimensionSpacePoint->toDimensionSpacePoint(), VisibilityConstraints::withoutRestrictions() )->findNodeByPath( @@ -86,9 +83,9 @@ public function findAdjustmentsForNodeType(NodeTypeName $nodeTypeName): \Generat $nodeAggregate->nodeAggregateId, StructureAdjustment::TETHERED_NODE_MISSING, 'The tethered child node "' . $tetheredNodeName->value . '" is missing.', - function () use ($nodeAggregate, $originDimensionSpacePoint, $tetheredNodeName, $expectedTetheredNodeType, $contentGraph) { + function () use ($nodeAggregate, $originDimensionSpacePoint, $tetheredNodeName, $expectedTetheredNodeType) { $events = $this->createEventsForMissingTetheredNode( - $contentGraph, + $this->projectedNodeIterator->contentGraph, $nodeAggregate, $originDimensionSpacePoint, $tetheredNodeName, @@ -112,7 +109,7 @@ function () use ($nodeAggregate, $originDimensionSpacePoint, $tetheredNodeName, } // find disallowed tethered nodes - $tetheredNodeAggregates = $contentGraph->findTetheredChildNodeAggregates( + $tetheredNodeAggregates = $this->projectedNodeIterator->contentGraph->findTetheredChildNodeAggregates( $nodeAggregate->nodeAggregateId ); foreach ($tetheredNodeAggregates as $tetheredNodeAggregate) { @@ -134,7 +131,7 @@ function () use ($tetheredNodeAggregate) { // find wrongly ordered tethered nodes if ($foundMissingOrDisallowedTetheredNodes === false) { foreach ($originDimensionSpacePoints as $originDimensionSpacePoint) { - $childNodes = $contentGraph->getSubgraph($originDimensionSpacePoint->toDimensionSpacePoint(), VisibilityConstraints::withoutRestrictions())->findChildNodes($nodeAggregate->nodeAggregateId, FindChildNodesFilter::create()); + $childNodes = $this->projectedNodeIterator->contentGraph->getSubgraph($originDimensionSpacePoint->toDimensionSpacePoint(), VisibilityConstraints::withoutRestrictions())->findChildNodes($nodeAggregate->nodeAggregateId, FindChildNodesFilter::create()); /** is indexed by node name, and the value is the tethered node itself */ $actualTetheredChildNodes = []; diff --git a/Neos.ContentRepository.StructureAdjustment/src/StructureAdjustmentService.php b/Neos.ContentRepository.StructureAdjustment/src/StructureAdjustmentService.php index f1b0222d607..3c671758d58 100644 --- a/Neos.ContentRepository.StructureAdjustment/src/StructureAdjustmentService.php +++ b/Neos.ContentRepository.StructureAdjustment/src/StructureAdjustmentService.php @@ -41,7 +41,6 @@ public function __construct( ); $this->tetheredNodeAdjustments = new TetheredNodeAdjustments( - $contentRepository, $projectedNodeIterator, $nodeTypeManager, $interDimensionalVariationGraph, diff --git a/Neos.ContentRepository.TestSuite/Classes/Unit/NodeSubjectProvider.php b/Neos.ContentRepository.TestSuite/Classes/Unit/NodeSubjectProvider.php index 61ce76d10d0..ac74c544c0b 100644 --- a/Neos.ContentRepository.TestSuite/Classes/Unit/NodeSubjectProvider.php +++ b/Neos.ContentRepository.TestSuite/Classes/Unit/NodeSubjectProvider.php @@ -35,10 +35,12 @@ use Neos\ContentRepository\Core\Projection\ContentGraph\Timestamps; use Neos\ContentRepository\Core\Projection\ContentGraph\VisibilityConstraints; use Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId; +use Neos\ContentRepository\Core\SharedModel\Node\NodeAddress; 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; +use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; use Symfony\Component\Serializer\Normalizer\BackedEnumNormalizer; use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer; use Symfony\Component\Serializer\Serializer; @@ -87,17 +89,13 @@ public function createMinimalNodeOfType( ): Node { $serializedDefaultPropertyValues = SerializedPropertyValues::defaultFromNodeType($nodeType, $this->propertyConverter); return Node::create( - ContentSubgraphIdentity::create( - ContentRepositoryId::fromString('default'), - ContentStreamId::fromString('cs-id'), - DimensionSpacePoint::createWithoutDimensions(), - VisibilityConstraints::withoutRestrictions() - ), + ContentRepositoryId::fromString('default'), + WorkspaceName::forLive(), + DimensionSpacePoint::createWithoutDimensions(), NodeAggregateId::create(), OriginDimensionSpacePoint::createWithoutDimensions(), NodeAggregateClassification::CLASSIFICATION_REGULAR, $nodeType->name, - $nodeType, new PropertyCollection( $propertyValues ? $serializedDefaultPropertyValues->merge($propertyValues) @@ -111,7 +109,10 @@ public function createMinimalNodeOfType( new \DateTimeImmutable(), new \DateTimeImmutable(), new \DateTimeImmutable() - ) + ), + VisibilityConstraints::withoutRestrictions(), + $nodeType, + ContentStreamId::fromString('cs-id'), ); } } diff --git a/Neos.ContentRepositoryRegistry/Classes/Command/ContentCommandController.php b/Neos.ContentRepositoryRegistry/Classes/Command/ContentCommandController.php index 87e8e0e936d..aebcfb0dd96 100644 --- a/Neos.ContentRepositoryRegistry/Classes/Command/ContentCommandController.php +++ b/Neos.ContentRepositoryRegistry/Classes/Command/ContentCommandController.php @@ -24,6 +24,7 @@ use Neos\ContentRepository\Core\Projection\ContentGraph\Filter\FindRootNodeAggregatesFilter; use Neos\ContentRepository\Core\Projection\ContentGraph\VisibilityConstraints; use Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId; +use Neos\ContentRepository\Core\SharedModel\Exception\WorkspaceDoesNotExist; use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId; use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; use Neos\ContentRepositoryRegistry\ContentRepositoryRegistry; @@ -141,23 +142,23 @@ public function createVariantsRecursivelyCommand(string $source, string $target, $contentRepositoryId = ContentRepositoryId::fromString($contentRepository); $sourceSpacePoint = DimensionSpacePoint::fromJsonString($source); $targetSpacePoint = OriginDimensionSpacePoint::fromJsonString($target); + $workspaceName = WorkspaceName::fromString($workspace); $contentRepositoryInstance = $this->contentRepositoryRegistry->get($contentRepositoryId); - $workspaceInstance = $contentRepositoryInstance->getWorkspaceFinder()->findOneByName(WorkspaceName::fromString($workspace)); - if ($workspaceInstance === null) { - $this->outputLine('Workspace "%s" does not exist', [$workspace]); + + try { + $sourceSubgraph = $contentRepositoryInstance->getContentGraph($workspaceName)->getSubgraph( + $sourceSpacePoint, + VisibilityConstraints::withoutRestrictions() + ); + } catch (WorkspaceDoesNotExist) { + $this->outputLine('Workspace "%s" does not exist', [$workspaceName->value]); $this->quit(1); } - $this->outputLine('Creating %s to %s in workspace %s (content repository %s)', [$sourceSpacePoint->toJson(), $targetSpacePoint->toJson(), $workspaceInstance->workspaceName->value, $contentRepositoryId->value]); - $this->outputLine('Resolved content stream %s', [$workspaceInstance->currentContentStreamId->value]); - - $sourceSubgraph = $contentRepositoryInstance->getContentGraph(WorkspaceName::fromString($workspace))->getSubgraph( - $sourceSpacePoint, - VisibilityConstraints::withoutRestrictions() - ); + $this->outputLine('Creating %s to %s in workspace %s (content repository %s)', [$sourceSpacePoint->toJson(), $targetSpacePoint->toJson(), $workspaceName->value, $contentRepositoryId->value]); - $rootNodeAggregates = $contentRepositoryInstance->getContentGraph($workspaceInstance->workspaceName) + $rootNodeAggregates = $contentRepositoryInstance->getContentGraph($workspaceName) ->findRootNodeAggregates(FindRootNodeAggregatesFilter::create()); @@ -168,7 +169,7 @@ public function createVariantsRecursivelyCommand(string $source, string $target, $rootNodeAggregate->nodeAggregateId, $sourceSubgraph, $targetSpacePoint, - $workspaceInstance->workspaceName, + $workspaceName, $contentRepositoryInstance, ) ); diff --git a/Neos.ContentRepositoryRegistry/Classes/ContentRepositoryRegistry.php b/Neos.ContentRepositoryRegistry/Classes/ContentRepositoryRegistry.php index a35269902f7..4900eb13451 100644 --- a/Neos.ContentRepositoryRegistry/Classes/ContentRepositoryRegistry.php +++ b/Neos.ContentRepositoryRegistry/Classes/ContentRepositoryRegistry.php @@ -15,7 +15,6 @@ use Neos\ContentRepository\Core\Projection\ContentGraph\Node; use Neos\ContentRepository\Core\Projection\ProjectionCatchUpTriggerInterface; use Neos\ContentRepository\Core\Projection\ProjectionFactoryInterface; -use Neos\ContentRepository\Core\Projection\Workspace\Workspace; use Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId; use Neos\ContentRepository\Core\SharedModel\User\UserIdProviderInterface; use Neos\ContentRepositoryRegistry\Exception\ContentRepositoryNotFoundException; @@ -93,15 +92,11 @@ public function resetFactoryInstance(ContentRepositoryId $contentRepositoryId): public function subgraphForNode(Node $node): ContentSubgraphInterface { - $contentRepository = $this->get($node->subgraphIdentity->contentRepositoryId); + $contentRepository = $this->get($node->contentRepositoryId); - // FIXME: node->workspace - /** @var Workspace $workspace */ - $workspace = $contentRepository->getWorkspaceFinder()->findOneByCurrentContentStreamId($node->subgraphIdentity->contentStreamId); - - return $contentRepository->getContentGraph($workspace->workspaceName)->getSubgraph( - $node->subgraphIdentity->dimensionSpacePoint, - $node->subgraphIdentity->visibilityConstraints + return $contentRepository->getContentGraph($node->workspaceName)->getSubgraph( + $node->dimensionSpacePoint, + $node->visibilityConstraints ); } diff --git a/Neos.Neos/Classes/Controller/Module/Management/WorkspacesController.php b/Neos.Neos/Classes/Controller/Module/Management/WorkspacesController.php index c603cd82dcf..c1c953efd24 100644 --- a/Neos.Neos/Classes/Controller/Module/Management/WorkspacesController.php +++ b/Neos.Neos/Classes/Controller/Module/Management/WorkspacesController.php @@ -870,10 +870,10 @@ protected function computeSiteChanges(Workspace $selectedWorkspace, ContentRepos */ protected function getOriginalNode( Node $modifiedNode, - WorkspaceName $workspaceName, + WorkspaceName $baseWorkspaceName, ContentRepository $contentRepository, ): ?Node { - $baseSubgraph = $contentRepository->getContentGraph($workspaceName)->getSubgraph( + $baseSubgraph = $contentRepository->getContentGraph($baseWorkspaceName)->getSubgraph( $modifiedNode->subgraphIdentity->dimensionSpacePoint, VisibilityConstraints::withoutRestrictions() ); diff --git a/Neos.Neos/Classes/Controller/Service/NodesController.php b/Neos.Neos/Classes/Controller/Service/NodesController.php index bff9d28db9d..c2905fb04a5 100644 --- a/Neos.Neos/Classes/Controller/Service/NodesController.php +++ b/Neos.Neos/Classes/Controller/Service/NodesController.php @@ -131,15 +131,6 @@ public function indexAction( unset($contextNode); if (is_null($nodeAddress)) { - $workspace = $contentRepository->getWorkspaceFinder()->findOneByName( - WorkspaceName::fromString($workspaceName) - ); - if (is_null($workspace)) { - throw new \InvalidArgumentException( - 'Could not resolve a node address for the given parameters.', - 1645631728 - ); - } $subgraph = $contentRepository->getContentGraph(WorkspaceName::fromString($workspaceName))->getSubgraph( DimensionSpacePoint::fromLegacyDimensionArray($dimensions), VisibilityConstraints::withoutRestrictions() // we are in a backend controller. @@ -204,12 +195,10 @@ public function showAction(string $identifier, string $workspaceName = 'live', a ->contentRepositoryId; $contentRepository = $this->contentRepositoryRegistry->get($contentRepositoryId); - $workspace = $contentRepository->getWorkspaceFinder() - ->findOneByName(WorkspaceName::fromString($workspaceName)); - assert($workspace instanceof Workspace); + $workspaceName = WorkspaceName::fromString($workspaceName); $dimensionSpacePoint = DimensionSpacePoint::fromLegacyDimensionArray($dimensions); - $subgraph = $contentRepository->getContentGraph(WorkspaceName::fromString($workspaceName)) + $subgraph = $contentRepository->getContentGraph($workspaceName) ->getSubgraph( $dimensionSpacePoint, VisibilityConstraints::withoutRestrictions() @@ -220,7 +209,7 @@ public function showAction(string $identifier, string $workspaceName = 'live', a if ($node === null) { $this->addExistingNodeVariantInformationToResponse( $nodeAggregateId, - $workspace->workspaceName, + $workspaceName, $dimensionSpacePoint, $contentRepository ); @@ -273,11 +262,9 @@ public function createAction( ->contentRepositoryId; $contentRepository = $this->contentRepositoryRegistry->get($contentRepositoryId); - $workspace = $contentRepository->getWorkspaceFinder() - ->findOneByName(WorkspaceName::fromString($workspaceName)); - assert($workspace instanceof Workspace); + $workspaceName = WorkspaceName::fromString($workspaceName); - $contentGraph = $contentRepository->getContentGraph(WorkspaceName::fromString($workspaceName)); + $contentGraph = $contentRepository->getContentGraph($workspaceName); $sourceSubgraph = $contentGraph ->getSubgraph( DimensionSpacePoint::fromLegacyDimensionArray($sourceDimensions), @@ -294,7 +281,7 @@ public function createAction( if ($mode === 'adoptFromAnotherDimension' || $mode === 'adoptFromAnotherDimensionAndCopyContent') { CatchUpTriggerWithSynchronousOption::synchronously(fn() => $this->adoptNodeAndParents( - $workspace->workspaceName, + $workspaceName, $nodeAggregateId, $sourceSubgraph, $targetSubgraph, @@ -305,7 +292,7 @@ public function createAction( $this->redirect('show', null, null, [ 'identifier' => $nodeAggregateId->value, - 'workspaceName' => $workspaceName, + 'workspaceName' => $workspaceName->value, 'dimensions' => $dimensions ]); } else { diff --git a/Neos.Neos/Classes/Domain/Service/SiteNodeUtility.php b/Neos.Neos/Classes/Domain/Service/SiteNodeUtility.php index 34614a86683..fb7829123bf 100644 --- a/Neos.Neos/Classes/Domain/Service/SiteNodeUtility.php +++ b/Neos.Neos/Classes/Domain/Service/SiteNodeUtility.php @@ -18,7 +18,6 @@ use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePoint; use Neos\ContentRepository\Core\Projection\ContentGraph\Node; use Neos\ContentRepository\Core\Projection\ContentGraph\VisibilityConstraints; -use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId; use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; use Neos\ContentRepositoryRegistry\ContentRepositoryRegistry; use Neos\Flow\Annotations as Flow; @@ -42,13 +41,9 @@ public function __construct( * To find the site node for the live workspace in a 0 dimensional content repository use: * * ```php - * $contentRepository = $this->contentRepositoryRegistry->get($site->getConfiguration()->contentRepositoryId); - * $liveWorkspace = $contentRepository->getWorkspaceFinder()->findOneByName(WorkspaceName::forLive()) - * ?? throw new \RuntimeException('Expected live workspace to exist.'); - * * $siteNode = $this->siteNodeUtility->findSiteNodeBySite( * $site, - * $liveWorkspace->currentContentStreamId, + * WorkspaceName::forLive(), * DimensionSpacePoint::createWithoutDimensions(), * VisibilityConstraints::frontend() * ); diff --git a/Neos.Neos/Classes/FrontendRouting/NodeAddress.php b/Neos.Neos/Classes/FrontendRouting/NodeAddress.php index 83db25d52c6..1126c00babe 100644 --- a/Neos.Neos/Classes/FrontendRouting/NodeAddress.php +++ b/Neos.Neos/Classes/FrontendRouting/NodeAddress.php @@ -31,7 +31,9 @@ * * It is used in Neos Routing to build a URI to a node. * - * @api + * @deprecated will be removed before Final 9.0 + * The NodeAddress was added 6 years ago without the concept of multiple crs + * Its usages will be replaced by the new node attached node address */ #[Flow\Proxy(false)] final readonly class NodeAddress diff --git a/Neos.Neos/Classes/FrontendRouting/NodeAddressFactory.php b/Neos.Neos/Classes/FrontendRouting/NodeAddressFactory.php index 3a2f28c2a7f..9781840a45a 100644 --- a/Neos.Neos/Classes/FrontendRouting/NodeAddressFactory.php +++ b/Neos.Neos/Classes/FrontendRouting/NodeAddressFactory.php @@ -22,7 +22,7 @@ use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; /** - * @api + * @deprecated will be removed before Final 9.0 */ class NodeAddressFactory { diff --git a/Neos.Neos/Classes/Fusion/Cache/NeosFusionContextSerializer.php b/Neos.Neos/Classes/Fusion/Cache/NeosFusionContextSerializer.php index 462aad66e6c..ac47b1e3172 100644 --- a/Neos.Neos/Classes/Fusion/Cache/NeosFusionContextSerializer.php +++ b/Neos.Neos/Classes/Fusion/Cache/NeosFusionContextSerializer.php @@ -4,12 +4,10 @@ namespace Neos\Neos\Fusion\Cache; -use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePoint; use Neos\ContentRepository\Core\Projection\ContentGraph\Node; use Neos\ContentRepository\Core\Projection\ContentGraph\VisibilityConstraints; -use Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId; -use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId; -use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; +use Neos\ContentRepository\Core\SharedModel\Exception\WorkspaceDoesNotExist; +use Neos\ContentRepository\Core\SharedModel\Node\NodeAddress; use Neos\ContentRepositoryRegistry\ContentRepositoryRegistry; use Neos\Fusion\Core\Cache\FusionContextSerializer; use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; @@ -47,6 +45,7 @@ public function __construct( public function denormalize(mixed $data, string $type, string $format = null, array $context = []) { if ($type === Node::class) { + /** @var $data array */ return $this->tryDeserializeNode($data); } return $this->fusionContextSerializer->denormalize($data, $type, $format, $context); @@ -65,30 +64,29 @@ public function normalize(mixed $object, string $format = null, array $context = } /** - * @param array $serializedNode + * @param array $serializedNode */ private function tryDeserializeNode(array $serializedNode): ?Node { - $contentRepositoryId = ContentRepositoryId::fromString($serializedNode['contentRepositoryId']); - - $contentRepository = $this->contentRepositoryRegistry->get($contentRepositoryId); - - $workspace = $contentRepository->getWorkspaceFinder()->findOneByName(WorkspaceName::fromString($serializedNode['workspaceName'])); - if (!$workspace) { + $nodeAddress = NodeAddress::fromArray($serializedNode); + + $contentRepository = $this->contentRepositoryRegistry->get($nodeAddress->contentRepositoryId); + + try { + $subgraph = $contentRepository->getContentGraph($nodeAddress->workspaceName)->getSubgraph( + $nodeAddress->dimensionSpacePoint, + $nodeAddress->workspaceName->isLive() + ? VisibilityConstraints::frontend() + : VisibilityConstraints::withoutRestrictions() + ); + } catch (WorkspaceDoesNotExist $exception) { // in case the workspace was deleted the rendering should probably not come to this very point // still if it does we fail silently // this is also the behaviour for when the property mapper is used return null; } - $subgraph = $contentRepository->getContentGraph($workspace->workspaceName)->getSubgraph( - DimensionSpacePoint::fromArray($serializedNode['dimensionSpacePoint']), - $workspace->isPublicWorkspace() - ? VisibilityConstraints::frontend() - : VisibilityConstraints::withoutRestrictions() - ); - - $node = $subgraph->findNodeById(NodeAggregateId::fromString($serializedNode['nodeAggregateId'])); + $node = $subgraph->findNodeById($nodeAddress->aggregateId); if (!$node) { // instead of crashing the whole rendering, by silently returning null we will most likely just break // rendering of the sub part here that needs the node @@ -103,22 +101,7 @@ private function tryDeserializeNode(array $serializedNode): ?Node */ private function serializeNode(Node $source): array { - $contentRepository = $this->contentRepositoryRegistry->get( - $source->subgraphIdentity->contentRepositoryId - ); - - $workspace = $contentRepository->getWorkspaceFinder()->findOneByCurrentContentStreamId($source->subgraphIdentity->contentStreamId); - - if (!$workspace) { - throw new \RuntimeException(sprintf('Could not fetch workspace for node (%s) in content stream (%s).', $source->nodeAggregateId->value, $source->subgraphIdentity->contentStreamId->value), 1699780153); - } - - return [ - 'contentRepositoryId' => $source->subgraphIdentity->contentRepositoryId->value, - 'workspaceName' => $workspace->workspaceName->value, - 'dimensionSpacePoint' => $source->subgraphIdentity->dimensionSpacePoint->jsonSerialize(), - 'nodeAggregateId' => $source->nodeAggregateId->value - ]; + return NodeAddress::fromNode($source)->jsonSerialize(); } public function supportsDenormalization(mixed $data, string $type, string $format = null) diff --git a/Neos.Neos/Classes/Fusion/DimensionsMenuItemsImplementation.php b/Neos.Neos/Classes/Fusion/DimensionsMenuItemsImplementation.php index 42b514c6f8a..0f0ada32061 100644 --- a/Neos.Neos/Classes/Fusion/DimensionsMenuItemsImplementation.php +++ b/Neos.Neos/Classes/Fusion/DimensionsMenuItemsImplementation.php @@ -9,6 +9,7 @@ use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePoint; use Neos\ContentRepository\Core\Projection\ContentGraph\ContentGraphInterface; use Neos\ContentRepository\Core\Projection\ContentGraph\Node; +use Neos\ContentRepository\Core\SharedModel\Exception\WorkspaceDoesNotExist; use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId; /** @@ -59,12 +60,11 @@ protected function buildItems(): array $interDimensionalVariationGraph = $dimensionMenuItemsImplementationInternals->interDimensionalVariationGraph; $currentDimensionSpacePoint = $currentNode->subgraphIdentity->dimensionSpacePoint; $contentDimensionIdentifierToLimitTo = $this->getContentDimensionIdentifierToLimitTo(); - // FIXME: node->workspaceName - $workspace = $contentRepository->getWorkspaceFinder()->findOneByCurrentContentStreamId($currentNode->subgraphIdentity->contentStreamId); - if (is_null($workspace)) { + try { + $contentGraph = $contentRepository->getContentGraph($currentNode->workspaceName); + } catch (WorkspaceDoesNotExist) { return $menuItems; } - $contentGraph = $contentRepository->getContentGraph($workspace->workspaceName); foreach ($interDimensionalVariationGraph->getDimensionSpacePoints() as $dimensionSpacePoint) { $variant = null; diff --git a/Neos.Neos/Classes/Fusion/Helper/CachingHelper.php b/Neos.Neos/Classes/Fusion/Helper/CachingHelper.php index 17cffd3c6a8..6da6b577a39 100644 --- a/Neos.Neos/Classes/Fusion/Helper/CachingHelper.php +++ b/Neos.Neos/Classes/Fusion/Helper/CachingHelper.php @@ -135,17 +135,15 @@ public function getWorkspaceChain(?Node $node): array } $contentRepository = $this->contentRepositoryRegistry->get( - $node->subgraphIdentity->contentRepositoryId + $node->contentRepositoryId ); - - /** @var Workspace $currentWorkspace */ - $currentWorkspace = $contentRepository->getWorkspaceFinder()->findOneByCurrentContentStreamId( - $node->subgraphIdentity->contentStreamId + $currentWorkspace = $contentRepository->getWorkspaceFinder()->findOneByName( + $node->workspaceName ); $workspaceChain = []; // TODO: Maybe write CTE here - while ($currentWorkspace instanceof Workspace) { + while ($currentWorkspace !== null) { $workspaceChain[$currentWorkspace->workspaceName->value] = $currentWorkspace; $currentWorkspace = $currentWorkspace->baseWorkspaceName ? $contentRepository->getWorkspaceFinder()->findOneByName($currentWorkspace->baseWorkspaceName) diff --git a/Neos.Neos/Classes/Fusion/Helper/DimensionHelper.php b/Neos.Neos/Classes/Fusion/Helper/DimensionHelper.php index ddcf6d6b269..de4c5b0b691 100644 --- a/Neos.Neos/Classes/Fusion/Helper/DimensionHelper.php +++ b/Neos.Neos/Classes/Fusion/Helper/DimensionHelper.php @@ -20,6 +20,7 @@ use Neos\ContentRepository\Core\Dimension\ContentDimensionValues; use Neos\ContentRepository\Core\Projection\ContentGraph\Node; use Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId; +use Neos\ContentRepository\Core\SharedModel\Exception\WorkspaceDoesNotExist; use Neos\ContentRepositoryRegistry\ContentRepositoryRegistry; use Neos\Eel\ProtectedContextAwareInterface; use Neos\Flow\Annotations as Flow; @@ -142,19 +143,18 @@ public function findVariantInDimension(Node $node, ContentDimensionId|string $di { $contentDimensionId = is_string($dimensionName) ? new ContentDimensionId($dimensionName) : $dimensionName; $contentDimensionValue = is_string($dimensionValue) ? new ContentDimensionValue($dimensionValue) : $dimensionValue; - $contentRepository = $this->contentRepositoryRegistry->get($node->subgraphIdentity->contentRepositoryId); - - $workspace = $contentRepository->getWorkspaceFinder()->findOneByCurrentContentStreamId($node->subgraphIdentity->contentStreamId); - if (is_null($workspace)) { + $contentRepository = $this->contentRepositoryRegistry->get($node->contentRepositoryId); + + try { + return $contentRepository + ->getContentGraph($node->workspaceName) + ->getSubgraph( + $node->dimensionSpacePoint->vary($contentDimensionId, $contentDimensionValue->value), + $node->visibilityConstraints + )->findNodeById($node->aggregateId); + } catch (WorkspaceDoesNotExist) { return null; } - // FIXME: node->workspaceName - return $contentRepository - ->getContentGraph($workspace->workspaceName) - ->getSubgraph( - $node->subgraphIdentity->dimensionSpacePoint->vary($contentDimensionId, $contentDimensionValue->value), - $node->subgraphIdentity->visibilityConstraints - )->findNodeById($node->nodeAggregateId); } public function allowsCallOfMethod($methodName): bool diff --git a/Neos.Neos/Classes/Service/LinkingService.php b/Neos.Neos/Classes/Service/LinkingService.php index 74f1c01ca94..e9553c778d9 100644 --- a/Neos.Neos/Classes/Service/LinkingService.php +++ b/Neos.Neos/Classes/Service/LinkingService.php @@ -350,10 +350,10 @@ public function createNodeUri( $this->lastLinkedNode = $node; $contentRepository = $this->contentRepositoryRegistry->get( - $node->subgraphIdentity->contentRepositoryId + $node->contentRepositoryId ); - $workspace = $contentRepository->getWorkspaceFinder()->findOneByCurrentContentStreamId( - $node->subgraphIdentity->contentStreamId + $workspace = $contentRepository->getWorkspaceFinder()->findOneByName( + $node->workspaceName ); $request = $controllerContext->getRequest()->getMainRequest(); $uriBuilder = clone $controllerContext->getUriBuilder(); diff --git a/Neos.Neos/Classes/View/FusionExceptionView.php b/Neos.Neos/Classes/View/FusionExceptionView.php index 6a0fa255278..fd6814acf20 100644 --- a/Neos.Neos/Classes/View/FusionExceptionView.php +++ b/Neos.Neos/Classes/View/FusionExceptionView.php @@ -16,6 +16,7 @@ use Neos\ContentRepository\Core\Projection\ContentGraph\Node; use Neos\ContentRepository\Core\Projection\ContentGraph\VisibilityConstraints; +use Neos\ContentRepository\Core\SharedModel\Exception\WorkspaceDoesNotExist; use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; use Neos\ContentRepositoryRegistry\ContentRepositoryRegistry; use Neos\Flow\Annotations as Flow; @@ -105,27 +106,26 @@ public function render(): ResponseInterface|StreamInterface return $this->renderErrorWelcomeScreen(); } - $contentRepository = $this->contentRepositoryRegistry->get($siteDetectionResult->contentRepositoryId); $fusionExceptionViewInternals = $this->contentRepositoryRegistry->buildService( $siteDetectionResult->contentRepositoryId, new FusionExceptionViewInternalsFactory() ); $dimensionSpacePoint = $fusionExceptionViewInternals->getArbitraryDimensionSpacePoint(); - $liveWorkspace = $contentRepository->getWorkspaceFinder()->findOneByName(WorkspaceName::forLive()); - - $currentSiteNode = null; $site = $this->siteRepository->findOneByNodeName($siteDetectionResult->siteNodeName); - if ($liveWorkspace && $site) { + + if (!$site) { + return $this->renderErrorWelcomeScreen(); + } + + try { $currentSiteNode = $this->siteNodeUtility->findSiteNodeBySite( $site, - $liveWorkspace->workspaceName, + WorkspaceName::forLive(), $dimensionSpacePoint, VisibilityConstraints::frontend() ); - } - - if (!$currentSiteNode) { + } catch (WorkspaceDoesNotExist | \RuntimeException) { return $this->renderErrorWelcomeScreen(); } diff --git a/Neos.Neos/Tests/Unit/Fusion/Helper/CachingHelperTest.php b/Neos.Neos/Tests/Unit/Fusion/Helper/CachingHelperTest.php index 39593920d4c..15a787667d8 100644 --- a/Neos.Neos/Tests/Unit/Fusion/Helper/CachingHelperTest.php +++ b/Neos.Neos/Tests/Unit/Fusion/Helper/CachingHelperTest.php @@ -14,6 +14,8 @@ use Neos\ContentRepository\Core\Projection\ContentGraph\Node; use Neos\ContentRepository\Core\Projection\ContentGraph\NodeTags; +use Neos\ContentRepository\Core\SharedModel\Node\NodeAddress; +use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; use Neos\ContentRepository\Domain\Model\Workspace; use Neos\ContentRepository\Domain\Service\Context; use Neos\Flow\Tests\UnitTestCase; @@ -198,21 +200,20 @@ private function createNode(NodeAggregateId $nodeAggregateId): Node { $now = new \DateTimeImmutable(); return Node::create( - ContentSubgraphIdentity::create( - ContentRepositoryId::fromString("default"), - ContentStreamId::fromString("cs-identifier"), - DimensionSpacePoint::createWithoutDimensions(), - VisibilityConstraints::withoutRestrictions() - ), + ContentRepositoryId::fromString("default"), + WorkspaceName::forLive(), + DimensionSpacePoint::createWithoutDimensions(), $nodeAggregateId, OriginDimensionSpacePoint::createWithoutDimensions(), NodeAggregateClassification::CLASSIFICATION_REGULAR, NodeTypeName::fromString("SomeNodeTypeName"), - null, new PropertyCollection(SerializedPropertyValues::fromArray([]), new PropertyConverter(new Serializer([], []))), null, NodeTags::createEmpty(), - Timestamps::create($now, $now, null, null) + Timestamps::create($now, $now, null, null), + VisibilityConstraints::withoutRestrictions(), + null, + ContentStreamId::fromString("cs-identifier"), ); } } diff --git a/Neos.TimeableNodeVisibility/Classes/Service/TimeableNodeVisibilityService.php b/Neos.TimeableNodeVisibility/Classes/Service/TimeableNodeVisibilityService.php index 70056016538..481a7f693f1 100644 --- a/Neos.TimeableNodeVisibility/Classes/Service/TimeableNodeVisibilityService.php +++ b/Neos.TimeableNodeVisibility/Classes/Service/TimeableNodeVisibilityService.php @@ -42,14 +42,9 @@ class TimeableNodeVisibilityService public function handleExceededNodeDates(ContentRepositoryId $contentRepositoryId, WorkspaceName $workspaceName): ChangedVisibilities { $contentRepository = $this->contentRepositoryRegistry->get($contentRepositoryId); - $liveWorkspace = $contentRepository->getWorkspaceFinder()->findOneByName($workspaceName); - if ($liveWorkspace === null) { - throw WorkspaceDoesNotExist::butWasSupposedTo($workspaceName); - } - $now = new \DateTimeImmutable(); - $nodes = $this->getNodesWithExceededDates($contentRepository, $liveWorkspace, $now); + $nodes = $this->getNodesWithExceededDates($contentRepository, $workspaceName, $now); $results = []; /** @var Node $node */ @@ -58,7 +53,7 @@ public function handleExceededNodeDates(ContentRepositoryId $contentRepositoryId if ($this->needsEnabling($node, $now) && $nodeIsDisabled) { $contentRepository->handle( EnableNodeAggregate::create( - $liveWorkspace->workspaceName, + $workspaceName, $node->nodeAggregateId, $node->subgraphIdentity->dimensionSpacePoint, NodeVariantSelectionStrategy::STRATEGY_ALL_SPECIALIZATIONS @@ -72,7 +67,7 @@ public function handleExceededNodeDates(ContentRepositoryId $contentRepositoryId if ($this->needsDisabling($node, $now) && !$nodeIsDisabled) { $contentRepository->handle( DisableNodeAggregate::create( - $liveWorkspace->workspaceName, + $workspaceName, $node->nodeAggregateId, $node->subgraphIdentity->dimensionSpacePoint, NodeVariantSelectionStrategy::STRATEGY_ALL_SPECIALIZATIONS @@ -89,13 +84,13 @@ public function handleExceededNodeDates(ContentRepositoryId $contentRepositoryId /** * @return \Generator */ - private function getNodesWithExceededDates(ContentRepository $contentRepository, Workspace $liveWorkspace, \DateTimeImmutable $now): \Generator + private function getNodesWithExceededDates(ContentRepository $contentRepository, WorkspaceName $workspaceName, \DateTimeImmutable $now): \Generator { $dimensionSpacePoints = $contentRepository->getVariationGraph()->getDimensionSpacePoints(); foreach ($dimensionSpacePoints as $dimensionSpacePoint) { - $contentGraph = $contentRepository->getContentGraph($liveWorkspace->workspaceName); + $contentGraph = $contentRepository->getContentGraph($workspaceName); // We fetch without restriction to get also all disabled nodes $subgraph = $contentGraph->getSubgraph(