From 47c9523a2859c4ecb18dbb8570e178133a150b22 Mon Sep 17 00:00:00 2001 From: Sebastian Kurfuerst Date: Tue, 14 Nov 2023 09:06:43 +0100 Subject: [PATCH] WIP --- .../Feature/Common/TetheredNodeInternals.php | 5 +- .../Dto/CoverageNodeVariantMapping.php | 76 ++++++++++++++++ .../Dto/CoverageNodeVariantMappings.php | 88 +++++++++++++++++++ .../Dto/EndSiblingVariantPosition.php | 61 +++++++++++++ .../Dto/SucceedingSiblingVariantPosition.php | 71 +++++++++++++++ .../Event/NodePeerVariantWasCreated.php | 37 +++++++- 6 files changed, 335 insertions(+), 3 deletions(-) create mode 100644 Neos.ContentRepository.Core/Classes/Feature/NodeVariation/Dto/CoverageNodeVariantMapping.php create mode 100644 Neos.ContentRepository.Core/Classes/Feature/NodeVariation/Dto/CoverageNodeVariantMappings.php create mode 100644 Neos.ContentRepository.Core/Classes/Feature/NodeVariation/Dto/EndSiblingVariantPosition.php create mode 100644 Neos.ContentRepository.Core/Classes/Feature/NodeVariation/Dto/SucceedingSiblingVariantPosition.php diff --git a/Neos.ContentRepository.Core/Classes/Feature/Common/TetheredNodeInternals.php b/Neos.ContentRepository.Core/Classes/Feature/Common/TetheredNodeInternals.php index ba8ff204f27..60833008816 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/Common/TetheredNodeInternals.php +++ b/Neos.ContentRepository.Core/Classes/Feature/Common/TetheredNodeInternals.php @@ -15,6 +15,7 @@ */ use Neos\ContentRepository\Core\ContentRepository; +use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePoint; use Neos\ContentRepository\Core\EventStore\Events; use Neos\ContentRepository\Core\Feature\NodeCreation\Event\NodeAggregateWithNodeWasCreated; use Neos\ContentRepository\Core\Feature\NodeModification\Dto\SerializedPropertyValues; @@ -84,12 +85,14 @@ protected function createEventsForMissingTetheredNode( foreach ($this->getInterDimensionalVariationGraph()->getRootGeneralizations() as $rootGeneralization) { $rootGeneralizationOrigin = OriginDimensionSpacePoint::fromDimensionSpacePoint($rootGeneralization); if ($creationOriginDimensionSpacePoint) { + $specializations = $this->getInterDimensionalVariationGraph()->getSpecializationSet($rootGeneralization); $events[] = new NodePeerVariantWasCreated( $parentNodeAggregate->contentStreamId, $tetheredNodeAggregateId, $creationOriginDimensionSpacePoint, $rootGeneralizationOrigin, - $this->getInterDimensionalVariationGraph()->getSpecializationSet($rootGeneralization) + $specializations, + array_map(fn(DimensionSpacePoint $dimensionSpacePoint) $specializations) ); } else { $events[] = new NodeAggregateWithNodeWasCreated( diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeVariation/Dto/CoverageNodeVariantMapping.php b/Neos.ContentRepository.Core/Classes/Feature/NodeVariation/Dto/CoverageNodeVariantMapping.php new file mode 100644 index 00000000000..73739e24a10 --- /dev/null +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeVariation/Dto/CoverageNodeVariantMapping.php @@ -0,0 +1,76 @@ + $array + */ + public static function fromArray(array $array): self + { + if (!empty($array['newSucceedingSibling'])) { + return new self( + DimensionSpacePoint::fromArray($array['coveredDimensionSpacePoint']), + SucceedingSiblingVariantPosition::fromArray($array['newSucceedingSibling']), + ); + } elseif (!empty($array['endSibling'])) { + return new self( + DimensionSpacePoint::fromArray($array['coveredDimensionSpacePoint']), + EndSiblingVariantPosition::create(), + ); + } else { + throw new \RuntimeException('!!!'); + } + } + + /** + * @return array + */ + public function jsonSerialize(): array + { + if ($this->destination instanceof SucceedingSiblingVariantPosition) { + return [ + 'coveredDimensionSpacePoint' => $this->coveredDimensionSpacePoint, + 'newSucceedingSibling' => $this->destination + ]; + } elseif ($this->destination instanceof EndSiblingVariantPosition) { + return [ + 'endSibling' => true, + ]; + } else { + throw new \RuntimeException('!!!'); + } + } +} diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeVariation/Dto/CoverageNodeVariantMappings.php b/Neos.ContentRepository.Core/Classes/Feature/NodeVariation/Dto/CoverageNodeVariantMappings.php new file mode 100644 index 00000000000..a1b609d3bf0 --- /dev/null +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeVariation/Dto/CoverageNodeVariantMappings.php @@ -0,0 +1,88 @@ + + * @api DTO of {@see NodeAggregateWasMoved} event + */ +final class CoverageNodeVariantMappings implements \IteratorAggregate, \Countable, \JsonSerializable +{ + /** + * @var array + */ + private array $mappings; + + /** + * @param array $values + */ + private function __construct(array $values) + { + $this->mappings = $values; + } + + /** + * @param array|CoverageNodeMoveMapping> $mappings + */ + public static function fromArray(array $mappings): self + { + $processedMappings = []; + foreach ($mappings as $mapping) { + if (is_array($mapping)) { + $processedMappings[] = CoverageNodeMoveMapping::fromArray($mapping); + } elseif ($mapping instanceof CoverageNodeMoveMapping) { + $processedMappings[] = $mapping; + } else { + /** @var mixed $mapping */ + throw new \InvalidArgumentException( + sprintf( + 'Invalid NodeMoveMapping. Expected instance of %s, got: %s', + CoverageNodeMoveMapping::class, + is_object($mapping) ? get_class($mapping) : gettype($mapping) + ), + 1547811318 + ); + } + } + return new self($processedMappings); + } + + public static function create(CoverageNodeMoveMapping ...$coverageNodeMoveMappings): self + { + return new self(array_values($coverageNodeMoveMappings)); + } + + + /** + * @return \Traversable + */ + public function getIterator(): \Traversable + { + yield from $this->mappings; + } + + public function count(): int + { + return count($this->mappings); + } + + /** + * @return array + */ + public function jsonSerialize(): array + { + return $this->mappings; + } +} diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeVariation/Dto/EndSiblingVariantPosition.php b/Neos.ContentRepository.Core/Classes/Feature/NodeVariation/Dto/EndSiblingVariantPosition.php new file mode 100644 index 00000000000..3fa993f8283 --- /dev/null +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeVariation/Dto/EndSiblingVariantPosition.php @@ -0,0 +1,61 @@ + $array + */ + public static function fromArray(array $array): self + { + return new self( + ); + } + + /** + * @return array + */ + public function jsonSerialize(): array + { + return [ + ]; + } + + public function toJson(): string + { + return json_encode($this, JSON_THROW_ON_ERROR); + } +} diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeVariation/Dto/SucceedingSiblingVariantPosition.php b/Neos.ContentRepository.Core/Classes/Feature/NodeVariation/Dto/SucceedingSiblingVariantPosition.php new file mode 100644 index 00000000000..3b90ed5a260 --- /dev/null +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeVariation/Dto/SucceedingSiblingVariantPosition.php @@ -0,0 +1,71 @@ + $array + */ + public static function fromArray(array $array): self + { + return new self( + NodeAggregateId::fromString($array['nodeAggregateId']), + OriginDimensionSpacePoint::fromArray($array['originDimensionSpacePoint']), + ); + } + + /** + * @return array + */ + public function jsonSerialize(): array + { + return [ + 'nodeAggregateId' => $this->nodeAggregateId, + 'originDimensionSpacePoint' => $this->originDimensionSpacePoint, + ]; + } + + public function toJson(): string + { + return json_encode($this, JSON_THROW_ON_ERROR); + } +} diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeVariation/Event/NodePeerVariantWasCreated.php b/Neos.ContentRepository.Core/Classes/Feature/NodeVariation/Event/NodePeerVariantWasCreated.php index afc1c70a3ce..e8687b656a6 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/NodeVariation/Event/NodePeerVariantWasCreated.php +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeVariation/Event/NodePeerVariantWasCreated.php @@ -19,6 +19,10 @@ use Neos\ContentRepository\Core\EventStore\EventInterface; use Neos\ContentRepository\Core\Feature\Common\EmbedsContentStreamAndNodeAggregateId; use Neos\ContentRepository\Core\Feature\Common\PublishableToOtherContentStreamsInterface; +use Neos\ContentRepository\Core\Feature\NodeMove\Dto\CoverageNodeMoveMapping; +use Neos\ContentRepository\Core\Feature\NodeVariation\Dto\CoverageNodeVariantMapping; +use Neos\ContentRepository\Core\Feature\NodeVariation\Dto\CoverageNodeVariantMappings; +use Neos\ContentRepository\Core\Feature\NodeVariation\Dto\EndSiblingVariantPosition; use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId; use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId; @@ -27,6 +31,16 @@ * * This event means: "Copy all properties (and references) of the given node from the source to the target dimension." * + * ## Positioning of Nodes across variants + * + * When translating content, e.g. from en to de, we effectively make a new node visible in all dimensions + * specified by {@see $peerCoverage} (i.e. we create **new edges** in the Content Graph). Because the edges + * contain positioning information ("I am 3rd child", ...), we need to specify exactly where the new edges + * should go to; as there might be different nodes and different sortings existing in all the different + * target dimensions. + * + * TODO: ALSO PARENT NODE AGG ID?? OR ONLY SUCC SIBLING?? ({@see SucceedingSiblingNodeMoveDestination}) + * * * ## Background: What is Node Variation? * @@ -87,8 +101,12 @@ public function __construct( * Because {@see $peerOrigin} can have nested dimensions underneath (e.g. in the example above from `en` to `de`) * this is the multiplied-out list of dimensions where the translation will be *visible in*. Will at least * contain {@see $peerOrigin}, but could have additional dimensions as well. + * + * TODO: REMOVE THIS, and create positionMappings from it? */ - public readonly DimensionSpacePointSet $peerCoverage, + public readonly DimensionSpacePointSet $peerCoverage, // DEPRECATED + + public readonly CoverageNodeVariantMappings $positionMappings, ) { } @@ -110,17 +128,32 @@ public function createCopyForContentStream(ContentStreamId $targetContentStreamI $this->sourceOrigin, $this->peerOrigin, $this->peerCoverage, + $this->positionMappings, ); } public static function fromArray(array $values): self { + if (!isset($values['positionMappings'])) { + // TODO: if peerCoverage and no positionMappings, create peerCoverage with type "end" + + // DEPRECATED + $coverageNodeVariantMappings = []; + foreach (DimensionSpacePointSet::fromArray($values['peerCoverage'])->getIterator() as $dimensionSpacePoint) { + $coverageNodeVariantMappings[] = CoverageNodeVariantMapping::createForNewEndSibling($dimensionSpacePoint, EndSiblingVariantPosition::create()); + } + $coverageNodeVariantMappings = CoverageNodeVariantMappings::create(...$coverageNodeVariantMappings); + } else { + $coverageNodeVariantMappings = CoverageNodeVariantMappings::fromArray($values['positionMappings']); + } + return new self( ContentStreamId::fromString($values['contentStreamId']), NodeAggregateId::fromString($values['nodeAggregateId']), OriginDimensionSpacePoint::fromArray($values['sourceOrigin']), OriginDimensionSpacePoint::fromArray($values['peerOrigin']), - DimensionSpacePointSet::fromArray($values['peerCoverage']), + DimensionSpacePointSet::fromArray($values['peerCoverage']), // DEPRECATED + $coverageNodeVariantMappings, ); }