Skip to content

Commit 5c916d4

Browse files
committed
FEATURE: Add support for setting references
1 parent 489eeca commit 5c916d4

10 files changed

Lines changed: 199 additions & 262 deletions

File tree

Classes/Domain/NodeCreation/NodeCreationService.php

Lines changed: 51 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,19 @@
77
use Flowpack\NodeTemplates\Domain\Template\RootTemplate;
88
use Flowpack\NodeTemplates\Domain\Template\Template;
99
use Flowpack\NodeTemplates\Domain\Template\Templates;
10-
use Neos\ContentRepository\Core\ContentRepository;
10+
use Neos\ContentRepository\Core\DimensionSpace\OriginDimensionSpacePoint;
1111
use Neos\ContentRepository\Core\Feature\NodeCreation\Command\CreateNodeAggregateWithNode;
1212
use Neos\ContentRepository\Core\Feature\NodeModification\Command\SetNodeProperties;
13+
use Neos\ContentRepository\Core\Feature\NodeModification\Dto\PropertyValuesToWrite;
14+
use Neos\ContentRepository\Core\Feature\NodeReferencing\Command\SetNodeReferences;
15+
use Neos\ContentRepository\Core\Feature\NodeReferencing\Dto\NodeReferencesToWrite;
1316
use Neos\ContentRepository\Core\NodeType\NodeTypeManager;
17+
use Neos\ContentRepository\Core\Projection\ContentGraph\ContentSubgraphInterface;
1418
use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId;
19+
use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateIds;
1520
use Neos\ContentRepository\Core\SharedModel\Node\NodeName;
21+
use Neos\ContentRepository\Core\SharedModel\Node\ReferenceName;
22+
use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId;
1623
use Neos\Flow\Annotations as Flow;
1724
use Neos\Neos\Ui\NodeCreationHandler\NodeCreationCommands;
1825
use Neos\Neos\Utility\NodeUriPathSegmentGenerator;
@@ -26,7 +33,7 @@ class NodeCreationService
2633
protected $nodeUriPathSegmentGenerator;
2734

2835
public function __construct(
29-
private readonly ContentRepository $contentRepository,
36+
private readonly ContentSubgraphInterface $subgraph,
3037
private readonly NodeTypeManager $nodeTypeManager
3138
) {
3239
}
@@ -45,14 +52,9 @@ public function apply(RootTemplate $template, NodeCreationCommands $commands, Ca
4552
$initialProperties = $commands->initialCreateCommand->initialPropertyValues;
4653

4754
$initialProperties = $initialProperties->merge(
48-
$propertiesAndReferences->requireValidProperties($nodeType, $caughtExceptions)
55+
PropertyValuesToWrite::fromArray($propertiesAndReferences->requireValidProperties($nodeType, $caughtExceptions))
4956
);
5057

51-
// todo set references
52-
// foreach ($propertiesAndReferences->requireValidReferences($nodeType, $commands->getContext(), $caughtExceptions) as $key => $value) {
53-
// $commands->setProperty($key, $value);
54-
// }
55-
5658
// $this->ensureNodeHasUriPathSegment($commands, $template);
5759
return $this->applyTemplateRecursively(
5860
$template->getChildNodes(),
@@ -62,7 +64,14 @@ public function apply(RootTemplate $template, NodeCreationCommands $commands, Ca
6264
$commands->initialCreateCommand->nodeAggregateId,
6365
$nodeType,
6466
),
65-
$commands->withInitialPropertyValues($initialProperties),
67+
$commands->withInitialPropertyValues($initialProperties)->withAdditionalCommands(
68+
...$this->createReferencesCommands(
69+
$commands->initialCreateCommand->contentStreamId,
70+
$commands->initialCreateCommand->nodeAggregateId,
71+
$commands->initialCreateCommand->originDimensionSpacePoint,
72+
$propertiesAndReferences->requireValidReferences($nodeType, $this->subgraph, $caughtExceptions)
73+
)
74+
),
6675
$caughtExceptions
6776
);
6877
}
@@ -89,12 +98,16 @@ private function applyTemplateRecursively(Templates $templates, ToBeCreatedNode
8998
$template->getName()
9099
),
91100
$parentNode->originDimensionSpacePoint,
92-
$propertiesAndReferences->requireValidProperties($nodeType, $caughtExceptions)
101+
PropertyValuesToWrite::fromArray($propertiesAndReferences->requireValidProperties($nodeType, $caughtExceptions))
102+
),
103+
...$this->createReferencesCommands(
104+
$parentNode->contentStreamId,
105+
$nodeAggregateId,
106+
$parentNode->originDimensionSpacePoint,
107+
$propertiesAndReferences->requireValidReferences($nodeType, $this->subgraph, $caughtExceptions)
93108
)
94109
);
95110

96-
// todo references
97-
98111
$commands = $this->applyTemplateRecursively(
99112
$template->getChildNodes(),
100113
$parentNode->withNodeTypeAndNodeAggregateId(
@@ -104,7 +117,6 @@ private function applyTemplateRecursively(Templates $templates, ToBeCreatedNode
104117
$commands,
105118
$caughtExceptions
106119
);
107-
108120
continue;
109121
}
110122

@@ -125,11 +137,8 @@ private function applyTemplateRecursively(Templates $templates, ToBeCreatedNode
125137

126138
$nodeType = $this->nodeTypeManager->getNodeType($template->getType());
127139

128-
129140
$propertiesAndReferences = PropertiesAndReferences::createFromArrayAndTypeDeclarations($template->getProperties(), $nodeType);
130141

131-
// hande references
132-
133142
$commands = $commands->withAdditionalCommands(
134143
new CreateNodeAggregateWithNode(
135144
$parentNode->contentStreamId,
@@ -138,14 +147,16 @@ private function applyTemplateRecursively(Templates $templates, ToBeCreatedNode
138147
$parentNode->originDimensionSpacePoint,
139148
$parentNode->nodeAggregateId,
140149
nodeName: NodeName::fromString(uniqid('node-', false)),
141-
initialPropertyValues: $propertiesAndReferences->requireValidProperties($nodeType, $caughtExceptions)
150+
initialPropertyValues: PropertyValuesToWrite::fromArray($propertiesAndReferences->requireValidProperties($nodeType, $caughtExceptions))
151+
),
152+
...$this->createReferencesCommands(
153+
$parentNode->contentStreamId,
154+
$nodeAggregateId,
155+
$parentNode->originDimensionSpacePoint,
156+
$propertiesAndReferences->requireValidReferences($nodeType, $this->subgraph, $caughtExceptions)
142157
)
143158
);
144159

145-
// set references
146-
// foreach ($propertiesAndReferences->requireValidReferences($nodeType, $node->getContext(), $caughtExceptions) as $key => $value) {
147-
// $node->setProperty($key, $value);
148-
// }
149160

150161
// $this->ensureNodeHasUriPathSegment($node, $template);
151162
$commands = $this->applyTemplateRecursively(
@@ -162,6 +173,25 @@ private function applyTemplateRecursively(Templates $templates, ToBeCreatedNode
162173
return $commands;
163174
}
164175

176+
/**
177+
* @param array<string, NodeAggregateIds> $references
178+
* @return list<SetNodeReferences>
179+
*/
180+
private function createReferencesCommands(ContentStreamId $contentStreamId, NodeAggregateId $nodeAggregateId, OriginDimensionSpacePoint $originDimensionSpacePoint, array $references): array
181+
{
182+
$commands = [];
183+
foreach ($references as $name => $nodeAggregateIds) {
184+
$commands[] = new SetNodeReferences(
185+
$contentStreamId,
186+
$nodeAggregateId,
187+
$originDimensionSpacePoint,
188+
ReferenceName::fromString($name),
189+
NodeReferencesToWrite::fromNodeAggregateIds($nodeAggregateIds)
190+
);
191+
}
192+
return $commands;
193+
}
194+
165195
/**
166196
* All document node types get a uri path segment; if it is not explicitly set in the properties,
167197
* it should be built based on the title property

Classes/Domain/NodeCreation/PropertiesAndReferences.php

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44

55
use Flowpack\NodeTemplates\Domain\ExceptionHandling\CaughtException;
66
use Flowpack\NodeTemplates\Domain\ExceptionHandling\CaughtExceptions;
7-
use Neos\ContentRepository\Core\Feature\NodeModification\Dto\PropertyValuesToWrite;
87
use Neos\ContentRepository\Core\NodeType\NodeType;
8+
use Neos\ContentRepository\Core\Projection\ContentGraph\ContentSubgraphInterface;
9+
use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateIds;
910
use Neos\Flow\Annotations as Flow;
1011

1112
/**
@@ -50,7 +51,7 @@ public static function createFromArrayAndTypeDeclarations(array $propertiesAndRe
5051
* This is a problem, as setting `null` might not be possible via the Neos UI and the Fusion rendering is most likely not going to handle this edge case.
5152
* Related discussion {@link https://github.com/Flowpack/Flowpack.NodeTemplates/issues/41}
5253
*/
53-
public function requireValidProperties(NodeType $nodeType, CaughtExceptions $caughtExceptions): PropertyValuesToWrite
54+
public function requireValidProperties(NodeType $nodeType, CaughtExceptions $caughtExceptions): array
5455
{
5556
$validProperties = [];
5657
foreach ($this->properties as $propertyName => $propertyValue) {
@@ -83,25 +84,29 @@ public function requireValidProperties(NodeType $nodeType, CaughtExceptions $cau
8384
);
8485
}
8586
}
86-
return PropertyValuesToWrite::fromArray($validProperties);
87+
return $validProperties;
8788
}
8889

89-
public function requireValidReferences(NodeType $nodeType, Context $subgraph, CaughtExceptions $caughtExceptions): array
90+
/**
91+
* @return array<string, NodeAggregateIds>
92+
*/
93+
public function requireValidReferences(NodeType $nodeType, ContentSubgraphInterface $subgraph, CaughtExceptions $caughtExceptions): array
9094
{
9195
$validReferences = [];
9296
foreach ($this->references as $referenceName => $referenceValue) {
9397
$referenceType = ReferenceType::fromPropertyOfNodeType($referenceName, $nodeType);
94-
if (!$referenceType->isMatchedBy($referenceValue, $subgraph)) {
95-
$caughtExceptions->add(CaughtException::fromException(new \RuntimeException(
96-
sprintf(
97-
'Reference could not be set, because node reference(s) %s cannot be resolved.',
98-
json_encode($referenceValue)
99-
),
100-
1685958176560
101-
))->withOrigin(sprintf('Reference "%s" in NodeType "%s"', $referenceName, $nodeType->getName())));
98+
try {
99+
$nodeAggregateIds = $referenceType->resolveNodeAggregateIds($referenceValue, $subgraph);
100+
} catch (\RuntimeException $runtimeException) {
101+
$caughtExceptions->add(
102+
CaughtException::fromException($runtimeException)
103+
->withOrigin(sprintf('Reference "%s" in NodeType "%s"', $referenceName, $nodeType->getName()))
104+
);
102105
continue;
103106
}
104-
$validReferences[$referenceName] = $referenceValue;
107+
if ($nodeAggregateIds->getIterator()->count()) {
108+
$validReferences[$referenceName] = $nodeAggregateIds;
109+
}
105110
}
106111
return $validReferences;
107112
}

Classes/Domain/NodeCreation/ReferenceType.php

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Neos\ContentRepository\Core\Projection\ContentGraph\ContentSubgraphInterface;
99
use Neos\ContentRepository\Core\Projection\ContentGraph\Node;
1010
use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId;
11+
use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateIds;
1112
use Neos\Flow\Annotations as Flow;
1213

1314
/**
@@ -74,29 +75,48 @@ public function getValue(): string
7475
return $this->value;
7576
}
7677

77-
public function isMatchedBy($propertyValue, ContentSubgraphInterface $subgraphForResolving): bool
78+
/**
79+
* @param mixed $referenceValue
80+
* @param ContentSubgraphInterface $subgraphForResolving
81+
* @throws \RuntimeException in case the $referenceValue could not be resolved to a node
82+
*/
83+
public function resolveNodeAggregateIds(mixed $referenceValue, ContentSubgraphInterface $subgraphForResolving): NodeAggregateIds
7884
{
79-
if ($propertyValue === null) {
80-
return true;
85+
if ($referenceValue === null) {
86+
return NodeAggregateIds::createEmpty();
8187
}
82-
$nodeAggregatesOrIds = $this->isReference() ? [$propertyValue] : $propertyValue;
88+
89+
$becauseOfInvalidValue = fn () => throw new \RuntimeException(
90+
sprintf(
91+
'Reference could not be set, because node reference(s) %s cannot be resolved.',
92+
json_encode($referenceValue)
93+
),
94+
1685958176560
95+
);
96+
97+
$nodeAggregatesOrIds = $this->isReference() ? [$referenceValue] : $referenceValue;
8398
if (is_array($nodeAggregatesOrIds) === false) {
84-
return false;
99+
throw $becauseOfInvalidValue();
85100
}
101+
102+
$nodeAggregateIds = [];
103+
86104
foreach ($nodeAggregatesOrIds as $singleNodeAggregateOrId) {
87105
if ($singleNodeAggregateOrId instanceof Node) {
106+
$nodeAggregateIds[] = $singleNodeAggregateOrId->nodeAggregateId;
88107
continue;
89108
}
90109
try {
91110
$singleNodeAggregateId = is_string($singleNodeAggregateOrId) ? NodeAggregateId::fromString($singleNodeAggregateOrId) : $singleNodeAggregateOrId;
92111
} catch (\Exception) {
93-
return false;
112+
throw $becauseOfInvalidValue();
94113
}
95-
if ($singleNodeAggregateId instanceof NodeAggregateId && $subgraphForResolving->findNodeById($singleNodeAggregateId) instanceof Node) {
114+
if ($singleNodeAggregateId instanceof NodeAggregateId && $subgraphForResolving->findNodeById($singleNodeAggregateId)) {
115+
$nodeAggregateIds[] = $singleNodeAggregateId;
96116
continue;
97117
}
98-
return false;
118+
throw $becauseOfInvalidValue();
99119
}
100-
return true;
120+
return NodeAggregateIds::fromArray($nodeAggregateIds);
101121
}
102122
}

Classes/Domain/TemplateNodeCreationHandler.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public function handle(
4848
$evaluationContext = [
4949
'data' => $data,
5050
// todo evaluate which context variables
51-
'subgraph' => $contentRepository->getContentGraph()->getSubgraph(
51+
'subgraph' => $subgraph = $contentRepository->getContentGraph()->getSubgraph(
5252
$commands->initialCreateCommand->contentStreamId,
5353
$commands->initialCreateCommand->originDimensionSpacePoint->toDimensionSpacePoint(),
5454
VisibilityConstraints::frontend()
@@ -60,7 +60,7 @@ public function handle(
6060
$template = $this->templateConfigurationProcessor->processTemplateConfiguration($templateConfiguration, $evaluationContext, $caughtExceptions);
6161
// $this->exceptionHandler->handleAfterTemplateConfigurationProcessing($caughtExceptions, $node);
6262

63-
return (new NodeCreationService($contentRepository, $contentRepository->getNodeTypeManager()))->apply($template, $commands, $caughtExceptions);
63+
return (new NodeCreationService($subgraph, $contentRepository->getNodeTypeManager()))->apply($template, $commands, $caughtExceptions);
6464
// $this->exceptionHandler->handleAfterNodeCreation($caughtExceptions, $node);
6565
} catch (TemplateNotCreatedException|TemplatePartiallyCreatedException $templateCreationException) {
6666
throw $templateCreationException;

0 commit comments

Comments
 (0)