From bb21d1270aa17109fefffa1dd20ba22f51ee09df Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Sun, 11 Jun 2023 14:53:37 +0200 Subject: [PATCH] FEATURE: Allow `NodeCreationHandlerInterface` to append additional commands --- .../Domain/Model/Changes/AbstractCreate.php | 23 +++-- .../ContentTitleNodeCreationHandler.php | 20 ++--- ...reationDialogPropertiesCreationHandler.php | 10 +-- .../DocumentTitleNodeCreationHandler.php | 18 ++-- .../NodeCreationCommands.php | 87 +++++++++++++++++++ .../NodeCreationHandlerInterface.php | 10 +-- 6 files changed, 125 insertions(+), 43 deletions(-) create mode 100644 Classes/NodeCreationHandler/NodeCreationCommands.php diff --git a/Classes/Domain/Model/Changes/AbstractCreate.php b/Classes/Domain/Model/Changes/AbstractCreate.php index 9cdf2cfe7a..ab492ace70 100644 --- a/Classes/Domain/Model/Changes/AbstractCreate.php +++ b/Classes/Domain/Model/Changes/AbstractCreate.php @@ -21,6 +21,7 @@ use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId; use Neos\ContentRepository\Core\SharedModel\Node\NodeName; use Neos\Neos\Ui\Exception\InvalidNodeCreationHandlerException; +use Neos\Neos\Ui\NodeCreationHandler\NodeCreationCommands; use Neos\Neos\Ui\NodeCreationHandler\NodeCreationHandlerInterface; abstract class AbstractCreate extends AbstractStructuralChange @@ -113,9 +114,17 @@ protected function createNode( ); $contentRepository = $this->contentRepositoryRegistry->get($parentNode->subgraphIdentity->contentRepositoryId); - $command = $this->applyNodeCreationHandlers($command, $nodeTypeName, $contentRepository); + $commands = $this->applyNodeCreationHandlers( + new NodeCreationCommands($command), + $nodeTypeName, + $contentRepository + ); + + foreach ($commands as $command) { + $contentRepository->handle($command) + ->block(); + } - $contentRepository->handle($command)->block(); /** @var Node $newlyCreatedNode */ $newlyCreatedNode = $this->contentRepositoryRegistry->subgraphForNode($parentNode) ->findChildNodeConnectedThroughEdgeName($parentNode->nodeAggregateId, $nodeName); @@ -131,15 +140,15 @@ protected function createNode( * @throws InvalidNodeCreationHandlerException */ protected function applyNodeCreationHandlers( - CreateNodeAggregateWithNode $command, + NodeCreationCommands $commands, NodeTypeName $nodeTypeName, ContentRepository $contentRepository - ): CreateNodeAggregateWithNode { + ): NodeCreationCommands { $data = $this->getData() ?: []; $nodeType = $contentRepository->getNodeTypeManager()->getNodeType($nodeTypeName); if (!isset($nodeType->getOptions()['nodeCreationHandlers']) || !is_array($nodeType->getOptions()['nodeCreationHandlers'])) { - return $command; + return $commands; } foreach ($nodeType->getOptions()['nodeCreationHandlers'] as $nodeCreationHandlerConfiguration) { $nodeCreationHandler = new $nodeCreationHandlerConfiguration['nodeCreationHandler'](); @@ -150,8 +159,8 @@ protected function applyNodeCreationHandlers( get_class($nodeCreationHandler) ), 1364759956); } - $command = $nodeCreationHandler->handle($command, $data, $contentRepository); + $commands = $nodeCreationHandler->handle($commands, $data, $contentRepository); } - return $command; + return $commands; } } diff --git a/Classes/NodeCreationHandler/ContentTitleNodeCreationHandler.php b/Classes/NodeCreationHandler/ContentTitleNodeCreationHandler.php index 8e42438abf..556f0ec918 100644 --- a/Classes/NodeCreationHandler/ContentTitleNodeCreationHandler.php +++ b/Classes/NodeCreationHandler/ContentTitleNodeCreationHandler.php @@ -12,11 +12,7 @@ */ use Neos\ContentRepository\Core\ContentRepository; -use Neos\ContentRepository\Core\NodeType\NodeTypeManager; use Neos\ContentRepository\Core\SharedModel\Exception\NodeTypeNotFoundException; -use Neos\ContentRepository\Core\Feature\NodeCreation\Command\CreateNodeAggregateWithNode; -use Neos\Flow\Annotations as Flow; -use Neos\Neos\Service\TransliterationService; /** * Node creation handler that sets the "title" property for new content elements according @@ -27,32 +23,26 @@ */ class ContentTitleNodeCreationHandler implements NodeCreationHandlerInterface { - /** - * @Flow\Inject - * @var TransliterationService - */ - protected $transliterationService; - /** * Set the node title for the newly created Content node * * @param array $data incoming data from the creationDialog * @throws NodeTypeNotFoundException */ - public function handle(CreateNodeAggregateWithNode $command, array $data, ContentRepository $contentRepository): CreateNodeAggregateWithNode + public function handle(NodeCreationCommands $commands, array $data, ContentRepository $contentRepository): NodeCreationCommands { if ( - !$contentRepository->getNodeTypeManager()->getNodeType($command->nodeTypeName) + !$contentRepository->getNodeTypeManager()->getNodeType($commands->first->nodeTypeName) ->isOfType('Neos.Neos:Content') ) { - return $command; + return $commands; } - $propertyValues = $command->initialPropertyValues; + $propertyValues = $commands->first->initialPropertyValues; if (isset($data['title'])) { $propertyValues = $propertyValues->withValue('title', $data['title']); } - return $command->withInitialPropertyValues($propertyValues); + return $commands->withInitialPropertyValues($propertyValues); } } diff --git a/Classes/NodeCreationHandler/CreationDialogPropertiesCreationHandler.php b/Classes/NodeCreationHandler/CreationDialogPropertiesCreationHandler.php index 6d3233db63..71c3e643c9 100644 --- a/Classes/NodeCreationHandler/CreationDialogPropertiesCreationHandler.php +++ b/Classes/NodeCreationHandler/CreationDialogPropertiesCreationHandler.php @@ -12,8 +12,6 @@ */ use Neos\ContentRepository\Core\ContentRepository; -use Neos\ContentRepository\Core\NodeType\NodeTypeManager; -use Neos\ContentRepository\Core\Feature\NodeCreation\Command\CreateNodeAggregateWithNode; use Neos\Flow\Annotations as Flow; use Neos\Flow\Property\PropertyMapper; use Neos\Flow\Property\TypeConverter\PersistentObjectConverter; @@ -35,14 +33,14 @@ class CreationDialogPropertiesCreationHandler implements NodeCreationHandlerInte /** * @param array $data */ - public function handle(CreateNodeAggregateWithNode $command, array $data, ContentRepository $contentRepository): CreateNodeAggregateWithNode + public function handle(NodeCreationCommands $commands, array $data, ContentRepository $contentRepository): NodeCreationCommands { $propertyMappingConfiguration = $this->propertyMapper->buildPropertyMappingConfiguration(); $propertyMappingConfiguration->forProperty('*')->allowAllProperties(); $propertyMappingConfiguration->setTypeConverterOption(PersistentObjectConverter::class, PersistentObjectConverter::CONFIGURATION_OVERRIDE_TARGET_TYPE_ALLOWED, true); - $nodeType = $contentRepository->getNodeTypeManager()->getNodeType($command->nodeTypeName); - $propertyValues = $command->initialPropertyValues; + $nodeType = $contentRepository->getNodeTypeManager()->getNodeType($commands->first->nodeTypeName); + $propertyValues = $commands->first->initialPropertyValues; foreach ($nodeType->getConfiguration('properties') as $propertyName => $propertyConfiguration) { if ( !isset($propertyConfiguration['ui']['showInCreationDialog']) @@ -62,6 +60,6 @@ public function handle(CreateNodeAggregateWithNode $command, array $data, Conten $propertyValues = $propertyValues->withValue($propertyName, $propertyValue); } - return $command->withInitialPropertyValues($propertyValues); + return $commands->withInitialPropertyValues($propertyValues); } } diff --git a/Classes/NodeCreationHandler/DocumentTitleNodeCreationHandler.php b/Classes/NodeCreationHandler/DocumentTitleNodeCreationHandler.php index 1bba052b23..373b03b451 100644 --- a/Classes/NodeCreationHandler/DocumentTitleNodeCreationHandler.php +++ b/Classes/NodeCreationHandler/DocumentTitleNodeCreationHandler.php @@ -16,8 +16,6 @@ use Behat\Transliterator\Transliterator; use Neos\ContentRepository\Core\Dimension\ContentDimensionId; use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePoint; -use Neos\ContentRepository\Core\Feature\NodeCreation\Command\CreateNodeAggregateWithNode; -use Neos\ContentRepository\Core\NodeType\NodeTypeManager; use Neos\Flow\I18n\Exception\InvalidLocaleIdentifierException; use Neos\Flow\I18n\Locale; use Neos\Neos\Service\TransliterationService; @@ -42,15 +40,15 @@ class DocumentTitleNodeCreationHandler implements NodeCreationHandlerInterface /** * @param array $data */ - public function handle(CreateNodeAggregateWithNode $command, array $data, ContentRepository $contentRepository): CreateNodeAggregateWithNode + public function handle(NodeCreationCommands $commands, array $data, ContentRepository $contentRepository): NodeCreationCommands { if ( - !$contentRepository->getNodeTypeManager()->getNodeType($command->nodeTypeName) + !$contentRepository->getNodeTypeManager()->getNodeType($commands->first->nodeTypeName) ->isOfType('Neos.Neos:Document') ) { - return $command; + return $commands; } - $propertyValues = $command->initialPropertyValues; + $propertyValues = $commands->first->initialPropertyValues; if (isset($data['title'])) { $propertyValues = $propertyValues->withValue('title', $data['title']); } @@ -59,14 +57,14 @@ public function handle(CreateNodeAggregateWithNode $command, array $data, Conten $uriPathSegment = $data['title']; // otherwise, we fall back to the node name - if ($uriPathSegment === null && $command->nodeName !== null) { - $uriPathSegment = $command->nodeName->value; + if ($uriPathSegment === null && $commands->first->nodeName !== null) { + $uriPathSegment = $commands->first->nodeName->value; } // if not empty, we transliterate the uriPathSegment according to the language of the new node if ($uriPathSegment !== null && $uriPathSegment !== '') { $uriPathSegment = $this->transliterateText( - $command->originDimensionSpacePoint->toDimensionSpacePoint(), + $commands->first->originDimensionSpacePoint->toDimensionSpacePoint(), $uriPathSegment ); } else { @@ -76,7 +74,7 @@ public function handle(CreateNodeAggregateWithNode $command, array $data, Conten $uriPathSegment = Transliterator::urlize($uriPathSegment); $propertyValues = $propertyValues->withValue('uriPathSegment', $uriPathSegment); - return $command->withInitialPropertyValues($propertyValues); + return $commands->withInitialPropertyValues($propertyValues); } private function transliterateText(DimensionSpacePoint $dimensionSpacePoint, string $text): string diff --git a/Classes/NodeCreationHandler/NodeCreationCommands.php b/Classes/NodeCreationHandler/NodeCreationCommands.php new file mode 100644 index 0000000000..2c502ebc77 --- /dev/null +++ b/Classes/NodeCreationHandler/NodeCreationCommands.php @@ -0,0 +1,87 @@ + + */ + public readonly array $additionalCommands; + + /** + * @internal to guarantee that the initial create command is mostly preserved as intended. + * You can use {@see self::withInitialPropertyValues()} to add new properties of the to be created node. + */ + public function __construct( + CreateNodeAggregateWithNode $first, + CreateNodeAggregateWithNode|SetNodeProperties|DisableNodeAggregate|EnableNodeAggregate|SetNodeReferences ...$additionalCommands + ) { + $this->first = $first; + $this->additionalCommands = $additionalCommands; + } + + /** + * Augment the first {@see CreateNodeAggregateWithNode} command with altered properties. + */ + public function withInitialPropertyValues(PropertyValuesToWrite $newInitialPropertyValues): self + { + return new self( + $this->first->withInitialPropertyValues($newInitialPropertyValues), + ...$this->additionalCommands + ); + } + + public function withAdditionalCommands( + CreateNodeAggregateWithNode|SetNodeProperties|DisableNodeAggregate|EnableNodeAggregate|SetNodeReferences ...$additionalCommands + ): self { + return new self($this->first, ...$this->additionalCommands, ...$additionalCommands); + } + + /** + * @return \Traversable + */ + public function getIterator(): \Traversable + { + yield $this->first; + yield from $this->additionalCommands; + } +} diff --git a/Classes/NodeCreationHandler/NodeCreationHandlerInterface.php b/Classes/NodeCreationHandler/NodeCreationHandlerInterface.php index db64c184da..907c51261f 100644 --- a/Classes/NodeCreationHandler/NodeCreationHandlerInterface.php +++ b/Classes/NodeCreationHandler/NodeCreationHandlerInterface.php @@ -12,7 +12,6 @@ */ use Neos\ContentRepository\Core\ContentRepository; -use Neos\ContentRepository\Core\Feature\NodeCreation\Command\CreateNodeAggregateWithNode; /** * Contract for Node Creation handler that allow to hook into the process just before a node is being added @@ -21,11 +20,12 @@ interface NodeCreationHandlerInterface { /** - * Do something with the newly created node + * You can "enrich" the node creation, by for example adding initial properties {@see NodeCreationCommands::withInitialPropertyValues()} + * or appending additional f.x. create-child nodes commands {@see NodeCreationCommands::withAdditionalCommands()} * - * @param CreateNodeAggregateWithNode $command The original node creation command + * @param NodeCreationCommands $commands original or previous commands, with the first command being the initial intended node creation * @param array $data incoming data from the creationDialog - * @return CreateNodeAggregateWithNode the original command or a new creation command with altered properties + * @return NodeCreationCommands the "enriched" commands, to be passed to the next handler or run at the end */ - public function handle(CreateNodeAggregateWithNode $command, array $data, ContentRepository $contentRepository): CreateNodeAggregateWithNode; + public function handle(NodeCreationCommands $commands, array $data, ContentRepository $contentRepository): NodeCreationCommands; }