Skip to content

Commit 6929f73

Browse files
committed
FEATURE: Allow NodeCreationHandlerInterface to append additional commands
1 parent 49a3c65 commit 6929f73

File tree

7 files changed

+130
-48
lines changed

7 files changed

+130
-48
lines changed

Classes/Domain/Model/Changes/AbstractCreate.php

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId;
2222
use Neos\ContentRepository\Core\SharedModel\Node\NodeName;
2323
use Neos\Neos\Ui\Exception\InvalidNodeCreationHandlerException;
24+
use Neos\Neos\Ui\NodeCreationHandler\NodeCreationCommands;
2425
use Neos\Neos\Ui\NodeCreationHandler\NodeCreationHandlerInterface;
2526
use Neos\Utility\PositionalArraySorter;
2627

@@ -114,9 +115,17 @@ protected function createNode(
114115
);
115116
$contentRepository = $this->contentRepositoryRegistry->get($parentNode->subgraphIdentity->contentRepositoryId);
116117

117-
$command = $this->applyNodeCreationHandlers($command, $nodeTypeName, $contentRepository);
118+
$commands = $this->applyNodeCreationHandlers(
119+
new NodeCreationCommands($command),
120+
$nodeTypeName,
121+
$contentRepository
122+
);
123+
124+
foreach ($commands as $command) {
125+
$contentRepository->handle($command)
126+
->block();
127+
}
118128

119-
$contentRepository->handle($command)->block();
120129
/** @var Node $newlyCreatedNode */
121130
$newlyCreatedNode = $this->contentRepositoryRegistry->subgraphForNode($parentNode)
122131
->findNodeById($nodeAggregateId);
@@ -132,15 +141,15 @@ protected function createNode(
132141
* @throws InvalidNodeCreationHandlerException
133142
*/
134143
protected function applyNodeCreationHandlers(
135-
CreateNodeAggregateWithNode $command,
144+
NodeCreationCommands $commands,
136145
NodeTypeName $nodeTypeName,
137146
ContentRepository $contentRepository
138-
): CreateNodeAggregateWithNode {
147+
): NodeCreationCommands {
139148
$data = $this->getData() ?: [];
140149
$nodeType = $contentRepository->getNodeTypeManager()->getNodeType($nodeTypeName);
141150
if (!isset($nodeType->getOptions()['nodeCreationHandlers'])
142151
|| !is_array($nodeType->getOptions()['nodeCreationHandlers'])) {
143-
return $command;
152+
return $commands;
144153
}
145154
foreach ((new PositionalArraySorter($nodeType->getOptions()['nodeCreationHandlers']))->toArray() as $nodeCreationHandlerConfiguration) {
146155
$nodeCreationHandler = new $nodeCreationHandlerConfiguration['nodeCreationHandler']();
@@ -151,8 +160,8 @@ protected function applyNodeCreationHandlers(
151160
get_class($nodeCreationHandler)
152161
), 1364759956);
153162
}
154-
$command = $nodeCreationHandler->handle($command, $data, $contentRepository);
163+
$commands = $nodeCreationHandler->handle($commands, $data, $contentRepository);
155164
}
156-
return $command;
165+
return $commands;
157166
}
158167
}

Classes/NodeCreationHandler/ContentTitleNodeCreationHandler.php

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,7 @@
1212
*/
1313

1414
use Neos\ContentRepository\Core\ContentRepository;
15-
use Neos\ContentRepository\Core\NodeType\NodeTypeManager;
1615
use Neos\ContentRepository\Core\SharedModel\Exception\NodeTypeNotFoundException;
17-
use Neos\ContentRepository\Core\Feature\NodeCreation\Command\CreateNodeAggregateWithNode;
18-
use Neos\Flow\Annotations as Flow;
19-
use Neos\Neos\Service\TransliterationService;
2016

2117
/**
2218
* Node creation handler that sets the "title" property for new content elements according
@@ -27,32 +23,26 @@
2723
*/
2824
class ContentTitleNodeCreationHandler implements NodeCreationHandlerInterface
2925
{
30-
/**
31-
* @Flow\Inject
32-
* @var TransliterationService
33-
*/
34-
protected $transliterationService;
35-
3626
/**
3727
* Set the node title for the newly created Content node
3828
*
3929
* @param array<string|int,mixed> $data incoming data from the creationDialog
4030
* @throws NodeTypeNotFoundException
4131
*/
42-
public function handle(CreateNodeAggregateWithNode $command, array $data, ContentRepository $contentRepository): CreateNodeAggregateWithNode
32+
public function handle(NodeCreationCommands $commands, array $data, ContentRepository $contentRepository): NodeCreationCommands
4333
{
4434
if (
45-
!$contentRepository->getNodeTypeManager()->getNodeType($command->nodeTypeName)
35+
!$contentRepository->getNodeTypeManager()->getNodeType($commands->first->nodeTypeName)
4636
->isOfType('Neos.Neos:Content')
4737
) {
48-
return $command;
38+
return $commands;
4939
}
5040

51-
$propertyValues = $command->initialPropertyValues;
41+
$propertyValues = $commands->first->initialPropertyValues;
5242
if (isset($data['title'])) {
5343
$propertyValues = $propertyValues->withValue('title', $data['title']);
5444
}
5545

56-
return $command->withInitialPropertyValues($propertyValues);
46+
return $commands->withInitialPropertyValues($propertyValues);
5747
}
5848
}

Classes/NodeCreationHandler/CreationDialogPropertiesCreationHandler.php

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@
1212
*/
1313

1414
use Neos\ContentRepository\Core\ContentRepository;
15-
use Neos\ContentRepository\Core\NodeType\NodeTypeManager;
16-
use Neos\ContentRepository\Core\Feature\NodeCreation\Command\CreateNodeAggregateWithNode;
1715
use Neos\Flow\Annotations as Flow;
1816
use Neos\Flow\Property\PropertyMapper;
1917
use Neos\Flow\Property\TypeConverter\PersistentObjectConverter;
@@ -35,14 +33,14 @@ class CreationDialogPropertiesCreationHandler implements NodeCreationHandlerInte
3533
/**
3634
* @param array<string|int,mixed> $data
3735
*/
38-
public function handle(CreateNodeAggregateWithNode $command, array $data, ContentRepository $contentRepository): CreateNodeAggregateWithNode
36+
public function handle(NodeCreationCommands $commands, array $data, ContentRepository $contentRepository): NodeCreationCommands
3937
{
4038
$propertyMappingConfiguration = $this->propertyMapper->buildPropertyMappingConfiguration();
4139
$propertyMappingConfiguration->forProperty('*')->allowAllProperties();
4240
$propertyMappingConfiguration->setTypeConverterOption(PersistentObjectConverter::class, PersistentObjectConverter::CONFIGURATION_OVERRIDE_TARGET_TYPE_ALLOWED, true);
4341

44-
$nodeType = $contentRepository->getNodeTypeManager()->getNodeType($command->nodeTypeName);
45-
$propertyValues = $command->initialPropertyValues;
42+
$nodeType = $contentRepository->getNodeTypeManager()->getNodeType($commands->first->nodeTypeName);
43+
$propertyValues = $commands->first->initialPropertyValues;
4644
foreach ($nodeType->getConfiguration('properties') as $propertyName => $propertyConfiguration) {
4745
if (
4846
!isset($propertyConfiguration['ui']['showInCreationDialog'])
@@ -62,6 +60,6 @@ public function handle(CreateNodeAggregateWithNode $command, array $data, Conten
6260
$propertyValues = $propertyValues->withValue($propertyName, $propertyValue);
6361
}
6462

65-
return $command->withInitialPropertyValues($propertyValues);
63+
return $commands->withInitialPropertyValues($propertyValues);
6664
}
6765
}

Classes/NodeCreationHandler/DocumentTitleNodeCreationHandler.php

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@
1616
use Behat\Transliterator\Transliterator;
1717
use Neos\ContentRepository\Core\Dimension\ContentDimensionId;
1818
use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePoint;
19-
use Neos\ContentRepository\Core\Feature\NodeCreation\Command\CreateNodeAggregateWithNode;
20-
use Neos\ContentRepository\Core\NodeType\NodeTypeManager;
2119
use Neos\Flow\I18n\Exception\InvalidLocaleIdentifierException;
2220
use Neos\Flow\I18n\Locale;
2321
use Neos\Neos\Service\TransliterationService;
@@ -42,15 +40,15 @@ class DocumentTitleNodeCreationHandler implements NodeCreationHandlerInterface
4240
/**
4341
* @param array<string|int,mixed> $data
4442
*/
45-
public function handle(CreateNodeAggregateWithNode $command, array $data, ContentRepository $contentRepository): CreateNodeAggregateWithNode
43+
public function handle(NodeCreationCommands $commands, array $data, ContentRepository $contentRepository): NodeCreationCommands
4644
{
4745
if (
48-
!$contentRepository->getNodeTypeManager()->getNodeType($command->nodeTypeName)
46+
!$contentRepository->getNodeTypeManager()->getNodeType($commands->first->nodeTypeName)
4947
->isOfType('Neos.Neos:Document')
5048
) {
51-
return $command;
49+
return $commands;
5250
}
53-
$propertyValues = $command->initialPropertyValues;
51+
$propertyValues = $commands->first->initialPropertyValues;
5452
if (isset($data['title'])) {
5553
$propertyValues = $propertyValues->withValue('title', $data['title']);
5654
}
@@ -59,14 +57,14 @@ public function handle(CreateNodeAggregateWithNode $command, array $data, Conten
5957
$uriPathSegment = $data['title'];
6058

6159
// otherwise, we fall back to the node name
62-
if ($uriPathSegment === null && $command->nodeName !== null) {
63-
$uriPathSegment = $command->nodeName->value;
60+
if ($uriPathSegment === null && $commands->first->nodeName !== null) {
61+
$uriPathSegment = $commands->first->nodeName->value;
6462
}
6563

6664
// if not empty, we transliterate the uriPathSegment according to the language of the new node
6765
if ($uriPathSegment !== null && $uriPathSegment !== '') {
6866
$uriPathSegment = $this->transliterateText(
69-
$command->originDimensionSpacePoint->toDimensionSpacePoint(),
67+
$commands->first->originDimensionSpacePoint->toDimensionSpacePoint(),
7068
$uriPathSegment
7169
);
7270
} else {
@@ -76,7 +74,7 @@ public function handle(CreateNodeAggregateWithNode $command, array $data, Conten
7674
$uriPathSegment = Transliterator::urlize($uriPathSegment);
7775
$propertyValues = $propertyValues->withValue('uriPathSegment', $uriPathSegment);
7876

79-
return $command->withInitialPropertyValues($propertyValues);
77+
return $commands->withInitialPropertyValues($propertyValues);
8078
}
8179

8280
private function transliterateText(DimensionSpacePoint $dimensionSpacePoint, string $text): string
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
<?php
2+
declare(strict_types=1);
3+
namespace Neos\Neos\Ui\NodeCreationHandler;
4+
5+
/*
6+
* This file is part of the Neos.Neos.Ui package.
7+
*
8+
* (c) Contributors of the Neos Project - www.neos.io
9+
*
10+
* This package is Open Source Software. For the full copyright and license
11+
* information, please view the LICENSE file which was distributed with this
12+
* source code.
13+
*/
14+
15+
use Neos\ContentRepository\Core\CommandHandler\CommandInterface;
16+
use Neos\ContentRepository\Core\Feature\NodeCreation\Command\CreateNodeAggregateWithNode;
17+
use Neos\ContentRepository\Core\Feature\NodeDisabling\Command\DisableNodeAggregate;
18+
use Neos\ContentRepository\Core\Feature\NodeDisabling\Command\EnableNodeAggregate;
19+
use Neos\ContentRepository\Core\Feature\NodeModification\Command\SetNodeProperties;
20+
use Neos\ContentRepository\Core\Feature\NodeModification\Dto\PropertyValuesToWrite;
21+
use Neos\ContentRepository\Core\Feature\NodeReferencing\Command\SetNodeReferences;
22+
23+
/**
24+
* A collection of commands that can be "enriched" via a {@see NodeCreationHandlerInterface}
25+
* The first command points to the initial node creation command.
26+
* It is ensured, that the initial node creation command, will be mostly preserved, to not contradict the users intend.
27+
*
28+
* Additional commands can be also appended, to be run after the initial node creation command.
29+
*
30+
* All commands will be executed blocking.
31+
*
32+
* @api except the constructor
33+
*/
34+
class NodeCreationCommands implements \IteratorAggregate
35+
{
36+
/**
37+
* The initial node creation command.
38+
* It is only allowed to change its properties via {@see self::withInitialPropertyValues()}
39+
*/
40+
public readonly CreateNodeAggregateWithNode $first;
41+
42+
/**
43+
* Add a list of commands that are executed after the initial created command was run.
44+
* This allows to create child-nodes and append other allowed commands.
45+
*
46+
* @var array<int|string, CreateNodeAggregateWithNode|SetNodeProperties|DisableNodeAggregate|EnableNodeAggregate|SetNodeReferences>
47+
*/
48+
public readonly array $additionalCommands;
49+
50+
/**
51+
* @internal to guarantee that the initial create command is mostly preserved as intended.
52+
* You can use {@see self::withInitialPropertyValues()} to add new properties of the to be created node.
53+
*/
54+
public function __construct(
55+
CreateNodeAggregateWithNode $first,
56+
CreateNodeAggregateWithNode|SetNodeProperties|DisableNodeAggregate|EnableNodeAggregate|SetNodeReferences ...$additionalCommands
57+
) {
58+
$this->first = $first;
59+
$this->additionalCommands = $additionalCommands;
60+
}
61+
62+
/**
63+
* Augment the first {@see CreateNodeAggregateWithNode} command with altered properties.
64+
*/
65+
public function withInitialPropertyValues(PropertyValuesToWrite $newInitialPropertyValues): self
66+
{
67+
return new self(
68+
$this->first->withInitialPropertyValues($newInitialPropertyValues),
69+
...$this->additionalCommands
70+
);
71+
}
72+
73+
public function withAdditionalCommands(
74+
CreateNodeAggregateWithNode|SetNodeProperties|DisableNodeAggregate|EnableNodeAggregate|SetNodeReferences ...$additionalCommands
75+
): self {
76+
return new self($this->first, ...$this->additionalCommands, ...$additionalCommands);
77+
}
78+
79+
/**
80+
* @return \Traversable<int, CommandInterface>
81+
*/
82+
public function getIterator(): \Traversable
83+
{
84+
yield $this->first;
85+
yield from $this->additionalCommands;
86+
}
87+
}

Classes/NodeCreationHandler/NodeCreationHandlerInterface.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
*/
1313

1414
use Neos\ContentRepository\Core\ContentRepository;
15-
use Neos\ContentRepository\Core\Feature\NodeCreation\Command\CreateNodeAggregateWithNode;
1615

1716
/**
1817
* Contract for Node Creation handler that allow to hook into the process just before a node is being added
@@ -21,11 +20,12 @@
2120
interface NodeCreationHandlerInterface
2221
{
2322
/**
24-
* Do something with the newly created node
23+
* You can "enrich" the node creation, by for example adding initial properties {@see NodeCreationCommands::withInitialPropertyValues()}
24+
* or appending additional f.x. create-child nodes commands {@see NodeCreationCommands::withAdditionalCommands()}
2525
*
26-
* @param CreateNodeAggregateWithNode $command The original node creation command
26+
* @param NodeCreationCommands $commands original or previous commands, with the first command being the initial intended node creation
2727
* @param array<string|int,mixed> $data incoming data from the creationDialog
28-
* @return CreateNodeAggregateWithNode the original command or a new creation command with altered properties
28+
* @return NodeCreationCommands the "enriched" commands, to be passed to the next handler or run at the end
2929
*/
30-
public function handle(CreateNodeAggregateWithNode $command, array $data, ContentRepository $contentRepository): CreateNodeAggregateWithNode;
30+
public function handle(NodeCreationCommands $commands, array $data, ContentRepository $contentRepository): NodeCreationCommands;
3131
}

Tests/IntegrationTests/SharedNodeTypesPackage/Classes/NodeCreationHandler/ImagePropertyNodeCreationHandler.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@
1212
*/
1313

1414
use Neos\ContentRepository\Core\ContentRepository;
15-
use Neos\ContentRepository\Core\Feature\NodeCreation\Command\CreateNodeAggregateWithNode;
1615
use Neos\Flow\Annotations as Flow;
1716
use Neos\Flow\Property\PropertyMapper;
1817
use Neos\Flow\Property\TypeConverter\PersistentObjectConverter;
1918
use Neos\Media\Domain\Model\ImageInterface;
19+
use Neos\Neos\Ui\NodeCreationHandler\NodeCreationCommands;
2020
use Neos\Neos\Ui\NodeCreationHandler\NodeCreationHandlerInterface;
2121

2222
class ImagePropertyNodeCreationHandler implements NodeCreationHandlerInterface
@@ -27,17 +27,17 @@ class ImagePropertyNodeCreationHandler implements NodeCreationHandlerInterface
2727
*/
2828
protected $propertyMapper;
2929

30-
public function handle(CreateNodeAggregateWithNode $command, array $data, ContentRepository $contentRepository): CreateNodeAggregateWithNode
30+
public function handle(NodeCreationCommands $commands, array $data, ContentRepository $contentRepository): NodeCreationCommands
3131
{
3232
if (!isset($data['image'])) {
33-
return $command;
33+
return $commands;
3434
}
3535
$propertyMappingConfiguration = $this->propertyMapper->buildPropertyMappingConfiguration();
3636
$propertyMappingConfiguration->forProperty('*')->allowAllProperties();
3737
$propertyMappingConfiguration->setTypeConverterOption(PersistentObjectConverter::class, PersistentObjectConverter::CONFIGURATION_OVERRIDE_TARGET_TYPE_ALLOWED, true);
3838
$image = $this->propertyMapper->convert($data['image'], ImageInterface::class, $propertyMappingConfiguration);
3939

40-
$propertyValues = $command->initialPropertyValues->withValue('image', $image);
41-
return $command->withInitialPropertyValues($propertyValues);
40+
$propertyValues = $commands->first->initialPropertyValues->withValue('image', $image);
41+
return $commands->withInitialPropertyValues($propertyValues);
4242
}
4343
}

0 commit comments

Comments
 (0)