Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

FEATURE: Node copying Version 3 (as a Neos service) #5371

Merged
merged 33 commits into from
Nov 19, 2024
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
0436ecf
WIP: Node Copy Version 3, first draft of NodeDuplicationService
mhsdesign Nov 17, 2024
2393125
WIP: Fix copy tethered node if leave node or nested
mhsdesign Nov 17, 2024
cc4ef55
Merge remote-tracking branch 'origin/9.0' into bugfix/copy-nodes-as-a…
mhsdesign Nov 18, 2024
870c35b
BUGFIX: Only leaf tethered nodes can be copied
mhsdesign Nov 18, 2024
d8cde95
TASK: Reintroduce reference copying for first node
mhsdesign Nov 18, 2024
ea753ca
TASK: Add further testcases for node copy
mhsdesign Nov 18, 2024
ded2e9d
TASK: Test and fix recursive node copying
mhsdesign Nov 18, 2024
7d39367
TASK: Add test for copying tethered nodes deep in the tree
mhsdesign Nov 18, 2024
05fb869
TASK: Add failing test for copying nested references
mhsdesign Nov 18, 2024
d7c70c3
TASK: Add failing test for copying nested tethered nodes
mhsdesign Nov 18, 2024
f61ab5e
BUGFIX: Fix copying nested tethered nodes
mhsdesign Nov 18, 2024
4abe078
BUGFIX: Copy references for child nodes on copy
mhsdesign Nov 18, 2024
c4447d3
TASK: Remove unused constraint check helpers from `TransientNodeCopy`
mhsdesign Nov 18, 2024
7e5fbf4
TASK: Move `NodeAggregateIdMapping` to Neos as it will be removed fro…
mhsdesign Nov 18, 2024
81aa60f
TASK: Make phpstan happy
mhsdesign Nov 18, 2024
37e9a51
TASK: Fix specifying `references` in a row
mhsdesign Nov 18, 2024
babc6cf
TASK: Add proper constraint-check exceptions
mhsdesign Nov 18, 2024
a4b1689
TASK: Remove todo regarding additional property values for first node
mhsdesign Nov 18, 2024
2fb6ab7
TASK: (re)Introduce `Subtrees` as typed array for `$children`
mhsdesign Nov 18, 2024
42c6b6a
!!! TASK: Soft removal of `CopyNodesRecursively`
mhsdesign Nov 18, 2024
d5d9dbe
TASK: Make `NodeAggregateIdMapping` usable and API, as its used in th…
mhsdesign Nov 18, 2024
3d5e963
TASK: Remove `$targetNodeName` from first level api option in `copyNo…
mhsdesign Nov 18, 2024
0096689
TASK: Move generic `Commands` into cr core
mhsdesign Nov 18, 2024
cb6b523
TASK: Document `NodeDuplicationService`
mhsdesign Nov 18, 2024
8d14bae
FEATURE: Copy subtree tags during copy
mhsdesign Nov 18, 2024
fdea0c6
BUGFIX: Migration to fix previously copied tethered nodes #5350
mhsdesign Nov 18, 2024
44c18dc
TASK: Introduce `migrateevents:copyNodesStatus`
mhsdesign Nov 18, 2024
8ae5755
TASK: Introduce `Subtrees::createEmpty`
mhsdesign Nov 19, 2024
38f336d
TASK: Adjust to new behat gherkin version to use quadruple back-slash
mhsdesign Nov 19, 2024
3de7d04
TASK: Add test for simple constraint checks
mhsdesign Nov 19, 2024
8523010
TASK: Introduce internal `calculateCopyNodesRecursively`
mhsdesign Nov 19, 2024
79794db
TASK: Rename `NodePath::forRoot` to `NodePath::createEmpty()`
mhsdesign Nov 19, 2024
d3115ce
TASK: Remove obsolete logic to keep tethered node ids
mhsdesign Nov 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
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\Subtrees;
use Neos\ContentRepository\Core\Projection\ContentGraph\VisibilityConstraints;
use Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId;
use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateClassification;
Expand Down Expand Up @@ -304,7 +305,7 @@ public function findSubtree(NodeAggregateId $entryNodeAggregateId, FindSubtreeFi
$this->dimensionSpacePoint,
$this->visibilityConstraints
);
$subtree = new Subtree((int)$nodeData['level'], $node, array_key_exists($nodeAggregateId, $subtreesByParentNodeId) ? array_reverse($subtreesByParentNodeId[$nodeAggregateId]) : []);
$subtree = Subtree::create((int)$nodeData['level'], $node, Subtrees::fromArray(array_key_exists($nodeAggregateId, $subtreesByParentNodeId) ? array_reverse($subtreesByParentNodeId[$nodeAggregateId]) : []));
Copy link
Member

@kitsunet kitsunet Nov 18, 2024

Choose a reason for hiding this comment

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

took me a moment here, I think I might prefer a Subtrees::empty() and then array_key_exists($nodeAggregateId, $subtreesByParentNodeId) ? Subtrees::fromArray(array_reverse($subtreesByParentNodeId[$nodeAggregateId])) : Subtrees:empty()

if ($subtree->level === 0) {
return $subtree;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
use Neos\ContentRepository\Core\Projection\ContentGraph\Reference;
use Neos\ContentRepository\Core\Projection\ContentGraph\References;
use Neos\ContentRepository\Core\Projection\ContentGraph\Subtree;
use Neos\ContentRepository\Core\Projection\ContentGraph\Subtrees;
use Neos\ContentRepository\Core\Projection\ContentGraph\Timestamps;
use Neos\ContentRepository\Core\Projection\ContentGraph\VisibilityConstraints;
use Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId;
Expand Down Expand Up @@ -154,7 +155,7 @@ public function mapNodeRowsToSubtree(
$nodeAggregateId = $nodeRow['nodeaggregateid'];
$parentNodeAggregateId = $nodeRow['parentnodeaggregateid'];
$node = $this->mapNodeRowToNode($nodeRow, $visibilityConstraints);
$subtree = new Subtree((int)$nodeRow['level'], $node, array_key_exists($nodeAggregateId, $subtreesByParentNodeId) ? array_reverse($subtreesByParentNodeId[$nodeAggregateId]) : []);
$subtree = Subtree::create((int)$nodeRow['level'], $node, Subtrees::fromArray(array_key_exists($nodeAggregateId, $subtreesByParentNodeId) ? array_reverse($subtreesByParentNodeId[$nodeAggregateId]) : []));
if ($subtree->level === 0) {
return $subtree;
}
Expand Down

This file was deleted.

62 changes: 62 additions & 0 deletions Neos.ContentRepository.Core/Classes/CommandHandler/Commands.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?php

declare(strict_types=1);

namespace Neos\ContentRepository\Core\CommandHandler;

/**
* @api can be used as collection of commands to be individually handled:
*
* foreach ($commands as $command) {
* $contentRepository->handle($command);
* }
*
* @implements \IteratorAggregate<CommandInterface>
*/
final readonly class Commands implements \IteratorAggregate, \Countable
{
/** @var array<int,CommandInterface> */
private array $items;

private function __construct(
CommandInterface ...$items
) {
$this->items = array_values($items);
}

public static function create(CommandInterface ...$items): self
{
return new self(...$items);
}

public static function createEmpty(): self
{
return new self();
}

/** @param array<CommandInterface> $array */
public static function fromArray(array $array): self
{
return new self(...$array);
}

public function append(CommandInterface $command): self
{
return new self(...[...$this->items, $command]);
}

public function merge(self $other): self
{
return new self(...$this->items, ...$other->items);
}

public function getIterator(): \Traversable
{
yield from $this->items;
}

public function count(): int
{
return count($this->items);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,17 @@

namespace Neos\ContentRepository\Core\Feature\NodeDuplication\Command;

use Neos\ContentRepository\Core\CommandHandler\CommandInterface;
use Neos\ContentRepository\Core\DimensionSpace\OriginDimensionSpacePoint;
use Neos\ContentRepository\Core\Feature\Common\MatchableWithNodeIdToPublishOrDiscardInterface;
use Neos\ContentRepository\Core\Feature\Common\RebasableToOtherWorkspaceInterface;
use Neos\ContentRepository\Core\Feature\NodeDuplication\Dto\NodeAggregateIdMapping;
use Neos\ContentRepository\Core\Feature\NodeDuplication\Dto\NodeSubtreeSnapshot;
use Neos\ContentRepository\Core\Feature\WorkspacePublication\Dto\NodeIdToPublishOrDiscard;
use Neos\ContentRepository\Core\Projection\ContentGraph\ContentSubgraphInterface;
use Neos\ContentRepository\Core\Projection\ContentGraph\Node;
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 Neos\Neos\Domain\Service\NodeDuplication\NodeAggregateIdMapping;

/**
* CopyNodesRecursively command
Expand All @@ -35,10 +33,10 @@
* The node will be appended as child node of the given `parentNodeId` which must cover the given
* `dimensionSpacePoint`.
*
* @api commands are the write-API of the ContentRepository
* @internal
* @deprecated with Neos 9 Beta 16, please use Neos's {@see \Neos\Neos\Domain\Service\NodeDuplicationService} instead.
*/
final readonly class CopyNodesRecursively implements
CommandInterface,
\JsonSerializable,
MatchableWithNodeIdToPublishOrDiscardInterface,
RebasableToOtherWorkspaceInterface
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ private function handleCopyNodesRecursively(

private function requireNewNodeAggregateIdsToNotExist(
ContentGraphInterface $contentGraph,
Dto\NodeAggregateIdMapping $nodeAggregateIdMapping
\Neos\Neos\Domain\Service\NodeDuplication\NodeAggregateIdMapping $nodeAggregateIdMapping
): void {
foreach ($nodeAggregateIdMapping->getAllNewNodeAggregateIds() as $nodeAggregateId) {
$this->requireProjectedNodeAggregateToNotExist(
Expand All @@ -191,7 +191,7 @@ private function createEventsForNodeToInsert(
?NodeAggregateId $targetSucceedingSiblingNodeAggregateId,
?NodeName $targetNodeName,
NodeSubtreeSnapshot $nodeToInsert,
Dto\NodeAggregateIdMapping $nodeAggregateIdMapping,
\Neos\Neos\Domain\Service\NodeDuplication\NodeAggregateIdMapping $nodeAggregateIdMapping,
array &$events,
): void {
$events[] = new NodeAggregateWithNodeWasCreated(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,25 @@
namespace Neos\ContentRepository\Core\Projection\ContentGraph;

/**
* @api returned by {@see ContentSubgraphInterface}
* @api returned by {@see ContentSubgraphInterface::findSubtree()}
*/
final readonly class Subtree
{
/**
* @param array<int,Subtree> $children
*/
public function __construct(
private function __construct(
public int $level,
public Node $node,
public array $children
public Subtrees $children
) {
}

/**
* @internal
*/
public static function create(
int $level,
Node $node,
Subtrees $children
): self {
return new self($level, $node, $children);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

declare(strict_types=1);

namespace Neos\ContentRepository\Core\Projection\ContentGraph;

/**
* @api part of {@see Subtree}
* @implements \IteratorAggregate<Subtree>
*/
final readonly class Subtrees implements \IteratorAggregate, \Countable
{
/** @var array<Subtree> */
private array $items;

private function __construct(
Subtree ...$items
) {
$this->items = $items;
}

/**
* @internal
*/
public static function create(Subtree ...$items): self
{
return new self(...$items);
}

/**
* @internal
* @param array<Subtree> $items
*/
public static function fromArray(array $items): self
{
return new self(...$items);
}

public function getIterator(): \Traversable
{
yield from $this->items;
}

public function count(): int
{
return count($this->items);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ trait CRTestSuiteTrait
use ContentStreamClosing;

use NodeCreation;
use NodeCopying;
use SubtreeTagging;
use NodeModification;
use NodeMove;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
use Neos\ContentRepository\Core\Feature\NodeCreation\Command\CreateNodeAggregateWithNode;
use Neos\ContentRepository\Core\Feature\NodeDisabling\Command\DisableNodeAggregate;
use Neos\ContentRepository\Core\Feature\NodeDisabling\Command\EnableNodeAggregate;
use Neos\ContentRepository\Core\Feature\NodeDuplication\Command\CopyNodesRecursively;
use Neos\ContentRepository\Core\Feature\NodeModification\Command\SetNodeProperties;
use Neos\ContentRepository\Core\Feature\NodeModification\Dto\PropertyValuesToWrite;
use Neos\ContentRepository\Core\Feature\NodeMove\Command\MoveNodeAggregate;
Expand Down Expand Up @@ -191,7 +190,9 @@ protected function addDefaultCommandArgumentValues(string $commandClassName, arr
}
}
if ($commandClassName === CreateNodeAggregateWithNode::class || $commandClassName === SetNodeReferences::class) {
if (is_array($commandArguments['references'] ?? null)) {
if (is_string($commandArguments['references'] ?? null)) {
$commandArguments['references'] = iterator_to_array($this->mapRawNodeReferencesToNodeReferencesToWrite(json_decode($commandArguments['references'], true, 512, JSON_THROW_ON_ERROR)));
} elseif (is_array($commandArguments['references'] ?? null)) {
$commandArguments['references'] = iterator_to_array($this->mapRawNodeReferencesToNodeReferencesToWrite($commandArguments['references']));
}
}
Expand Down Expand Up @@ -243,7 +244,6 @@ protected static function resolveShortCommandName(string $shortCommandName): str
ChangeBaseWorkspace::class,
ChangeNodeAggregateName::class,
ChangeNodeAggregateType::class,
CopyNodesRecursively::class,
CreateNodeAggregateWithNode::class,
CreateNodeVariant::class,
CreateRootNodeAggregateWithNode::class,
Expand Down
Loading
Loading