Skip to content

Commit

Permalink
FEATURE: Add support for setting references
Browse files Browse the repository at this point in the history
  • Loading branch information
mhsdesign committed Jun 9, 2023
1 parent 489eeca commit 5c916d4
Show file tree
Hide file tree
Showing 10 changed files with 199 additions and 262 deletions.
72 changes: 51 additions & 21 deletions Classes/Domain/NodeCreation/NodeCreationService.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,19 @@
use Flowpack\NodeTemplates\Domain\Template\RootTemplate;
use Flowpack\NodeTemplates\Domain\Template\Template;
use Flowpack\NodeTemplates\Domain\Template\Templates;
use Neos\ContentRepository\Core\ContentRepository;
use Neos\ContentRepository\Core\DimensionSpace\OriginDimensionSpacePoint;
use Neos\ContentRepository\Core\Feature\NodeCreation\Command\CreateNodeAggregateWithNode;
use Neos\ContentRepository\Core\Feature\NodeModification\Command\SetNodeProperties;
use Neos\ContentRepository\Core\Feature\NodeModification\Dto\PropertyValuesToWrite;
use Neos\ContentRepository\Core\Feature\NodeReferencing\Command\SetNodeReferences;
use Neos\ContentRepository\Core\Feature\NodeReferencing\Dto\NodeReferencesToWrite;
use Neos\ContentRepository\Core\NodeType\NodeTypeManager;
use Neos\ContentRepository\Core\Projection\ContentGraph\ContentSubgraphInterface;
use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId;
use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateIds;
use Neos\ContentRepository\Core\SharedModel\Node\NodeName;
use Neos\ContentRepository\Core\SharedModel\Node\ReferenceName;
use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId;
use Neos\Flow\Annotations as Flow;
use Neos\Neos\Ui\NodeCreationHandler\NodeCreationCommands;
use Neos\Neos\Utility\NodeUriPathSegmentGenerator;
Expand All @@ -26,7 +33,7 @@ class NodeCreationService
protected $nodeUriPathSegmentGenerator;

public function __construct(
private readonly ContentRepository $contentRepository,
private readonly ContentSubgraphInterface $subgraph,
private readonly NodeTypeManager $nodeTypeManager
) {
}
Expand All @@ -45,14 +52,9 @@ public function apply(RootTemplate $template, NodeCreationCommands $commands, Ca
$initialProperties = $commands->initialCreateCommand->initialPropertyValues;

$initialProperties = $initialProperties->merge(
$propertiesAndReferences->requireValidProperties($nodeType, $caughtExceptions)
PropertyValuesToWrite::fromArray($propertiesAndReferences->requireValidProperties($nodeType, $caughtExceptions))
);

// todo set references
// foreach ($propertiesAndReferences->requireValidReferences($nodeType, $commands->getContext(), $caughtExceptions) as $key => $value) {
// $commands->setProperty($key, $value);
// }

// $this->ensureNodeHasUriPathSegment($commands, $template);
return $this->applyTemplateRecursively(
$template->getChildNodes(),
Expand All @@ -62,7 +64,14 @@ public function apply(RootTemplate $template, NodeCreationCommands $commands, Ca
$commands->initialCreateCommand->nodeAggregateId,
$nodeType,
),
$commands->withInitialPropertyValues($initialProperties),
$commands->withInitialPropertyValues($initialProperties)->withAdditionalCommands(
...$this->createReferencesCommands(
$commands->initialCreateCommand->contentStreamId,
$commands->initialCreateCommand->nodeAggregateId,
$commands->initialCreateCommand->originDimensionSpacePoint,
$propertiesAndReferences->requireValidReferences($nodeType, $this->subgraph, $caughtExceptions)
)
),
$caughtExceptions
);
}
Expand All @@ -89,12 +98,16 @@ private function applyTemplateRecursively(Templates $templates, ToBeCreatedNode
$template->getName()
),
$parentNode->originDimensionSpacePoint,
$propertiesAndReferences->requireValidProperties($nodeType, $caughtExceptions)
PropertyValuesToWrite::fromArray($propertiesAndReferences->requireValidProperties($nodeType, $caughtExceptions))
),
...$this->createReferencesCommands(
$parentNode->contentStreamId,
$nodeAggregateId,
$parentNode->originDimensionSpacePoint,
$propertiesAndReferences->requireValidReferences($nodeType, $this->subgraph, $caughtExceptions)
)
);

// todo references

$commands = $this->applyTemplateRecursively(
$template->getChildNodes(),
$parentNode->withNodeTypeAndNodeAggregateId(
Expand All @@ -104,7 +117,6 @@ private function applyTemplateRecursively(Templates $templates, ToBeCreatedNode
$commands,
$caughtExceptions
);

continue;
}

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

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


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

// hande references

$commands = $commands->withAdditionalCommands(
new CreateNodeAggregateWithNode(
$parentNode->contentStreamId,
Expand All @@ -138,14 +147,16 @@ private function applyTemplateRecursively(Templates $templates, ToBeCreatedNode
$parentNode->originDimensionSpacePoint,
$parentNode->nodeAggregateId,
nodeName: NodeName::fromString(uniqid('node-', false)),
initialPropertyValues: $propertiesAndReferences->requireValidProperties($nodeType, $caughtExceptions)
initialPropertyValues: PropertyValuesToWrite::fromArray($propertiesAndReferences->requireValidProperties($nodeType, $caughtExceptions))
),
...$this->createReferencesCommands(
$parentNode->contentStreamId,
$nodeAggregateId,
$parentNode->originDimensionSpacePoint,
$propertiesAndReferences->requireValidReferences($nodeType, $this->subgraph, $caughtExceptions)
)
);

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

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

/**
* @param array<string, NodeAggregateIds> $references
* @return list<SetNodeReferences>
*/
private function createReferencesCommands(ContentStreamId $contentStreamId, NodeAggregateId $nodeAggregateId, OriginDimensionSpacePoint $originDimensionSpacePoint, array $references): array
{
$commands = [];
foreach ($references as $name => $nodeAggregateIds) {
$commands[] = new SetNodeReferences(
$contentStreamId,
$nodeAggregateId,
$originDimensionSpacePoint,
ReferenceName::fromString($name),
NodeReferencesToWrite::fromNodeAggregateIds($nodeAggregateIds)
);
}
return $commands;
}

/**
* All document node types get a uri path segment; if it is not explicitly set in the properties,
* it should be built based on the title property
Expand Down
31 changes: 18 additions & 13 deletions Classes/Domain/NodeCreation/PropertiesAndReferences.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@

use Flowpack\NodeTemplates\Domain\ExceptionHandling\CaughtException;
use Flowpack\NodeTemplates\Domain\ExceptionHandling\CaughtExceptions;
use Neos\ContentRepository\Core\Feature\NodeModification\Dto\PropertyValuesToWrite;
use Neos\ContentRepository\Core\NodeType\NodeType;
use Neos\ContentRepository\Core\Projection\ContentGraph\ContentSubgraphInterface;
use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateIds;
use Neos\Flow\Annotations as Flow;

/**
Expand Down Expand Up @@ -50,7 +51,7 @@ public static function createFromArrayAndTypeDeclarations(array $propertiesAndRe
* 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.
* Related discussion {@link https://github.com/Flowpack/Flowpack.NodeTemplates/issues/41}
*/
public function requireValidProperties(NodeType $nodeType, CaughtExceptions $caughtExceptions): PropertyValuesToWrite
public function requireValidProperties(NodeType $nodeType, CaughtExceptions $caughtExceptions): array
{
$validProperties = [];
foreach ($this->properties as $propertyName => $propertyValue) {
Expand Down Expand Up @@ -83,25 +84,29 @@ public function requireValidProperties(NodeType $nodeType, CaughtExceptions $cau
);
}
}
return PropertyValuesToWrite::fromArray($validProperties);
return $validProperties;
}

public function requireValidReferences(NodeType $nodeType, Context $subgraph, CaughtExceptions $caughtExceptions): array
/**
* @return array<string, NodeAggregateIds>
*/
public function requireValidReferences(NodeType $nodeType, ContentSubgraphInterface $subgraph, CaughtExceptions $caughtExceptions): array
{
$validReferences = [];
foreach ($this->references as $referenceName => $referenceValue) {
$referenceType = ReferenceType::fromPropertyOfNodeType($referenceName, $nodeType);
if (!$referenceType->isMatchedBy($referenceValue, $subgraph)) {
$caughtExceptions->add(CaughtException::fromException(new \RuntimeException(
sprintf(
'Reference could not be set, because node reference(s) %s cannot be resolved.',
json_encode($referenceValue)
),
1685958176560
))->withOrigin(sprintf('Reference "%s" in NodeType "%s"', $referenceName, $nodeType->getName())));
try {
$nodeAggregateIds = $referenceType->resolveNodeAggregateIds($referenceValue, $subgraph);
} catch (\RuntimeException $runtimeException) {
$caughtExceptions->add(
CaughtException::fromException($runtimeException)
->withOrigin(sprintf('Reference "%s" in NodeType "%s"', $referenceName, $nodeType->getName()))
);
continue;
}
$validReferences[$referenceName] = $referenceValue;
if ($nodeAggregateIds->getIterator()->count()) {
$validReferences[$referenceName] = $nodeAggregateIds;
}
}
return $validReferences;
}
Expand Down
38 changes: 29 additions & 9 deletions Classes/Domain/NodeCreation/ReferenceType.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
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\NodeAggregateIds;
use Neos\Flow\Annotations as Flow;

/**
Expand Down Expand Up @@ -74,29 +75,48 @@ public function getValue(): string
return $this->value;
}

public function isMatchedBy($propertyValue, ContentSubgraphInterface $subgraphForResolving): bool
/**
* @param mixed $referenceValue
* @param ContentSubgraphInterface $subgraphForResolving
* @throws \RuntimeException in case the $referenceValue could not be resolved to a node
*/
public function resolveNodeAggregateIds(mixed $referenceValue, ContentSubgraphInterface $subgraphForResolving): NodeAggregateIds
{
if ($propertyValue === null) {
return true;
if ($referenceValue === null) {
return NodeAggregateIds::createEmpty();
}
$nodeAggregatesOrIds = $this->isReference() ? [$propertyValue] : $propertyValue;

$becauseOfInvalidValue = fn () => throw new \RuntimeException(
sprintf(
'Reference could not be set, because node reference(s) %s cannot be resolved.',
json_encode($referenceValue)
),
1685958176560
);

$nodeAggregatesOrIds = $this->isReference() ? [$referenceValue] : $referenceValue;
if (is_array($nodeAggregatesOrIds) === false) {
return false;
throw $becauseOfInvalidValue();
}

$nodeAggregateIds = [];

foreach ($nodeAggregatesOrIds as $singleNodeAggregateOrId) {
if ($singleNodeAggregateOrId instanceof Node) {
$nodeAggregateIds[] = $singleNodeAggregateOrId->nodeAggregateId;
continue;
}
try {
$singleNodeAggregateId = is_string($singleNodeAggregateOrId) ? NodeAggregateId::fromString($singleNodeAggregateOrId) : $singleNodeAggregateOrId;
} catch (\Exception) {
return false;
throw $becauseOfInvalidValue();
}
if ($singleNodeAggregateId instanceof NodeAggregateId && $subgraphForResolving->findNodeById($singleNodeAggregateId) instanceof Node) {
if ($singleNodeAggregateId instanceof NodeAggregateId && $subgraphForResolving->findNodeById($singleNodeAggregateId)) {
$nodeAggregateIds[] = $singleNodeAggregateId;
continue;
}
return false;
throw $becauseOfInvalidValue();
}
return true;
return NodeAggregateIds::fromArray($nodeAggregateIds);
}
}
4 changes: 2 additions & 2 deletions Classes/Domain/TemplateNodeCreationHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public function handle(
$evaluationContext = [
'data' => $data,
// todo evaluate which context variables
'subgraph' => $contentRepository->getContentGraph()->getSubgraph(
'subgraph' => $subgraph = $contentRepository->getContentGraph()->getSubgraph(
$commands->initialCreateCommand->contentStreamId,
$commands->initialCreateCommand->originDimensionSpacePoint->toDimensionSpacePoint(),
VisibilityConstraints::frontend()
Expand All @@ -60,7 +60,7 @@ public function handle(
$template = $this->templateConfigurationProcessor->processTemplateConfiguration($templateConfiguration, $evaluationContext, $caughtExceptions);
// $this->exceptionHandler->handleAfterTemplateConfigurationProcessing($caughtExceptions, $node);

return (new NodeCreationService($contentRepository, $contentRepository->getNodeTypeManager()))->apply($template, $commands, $caughtExceptions);
return (new NodeCreationService($subgraph, $contentRepository->getNodeTypeManager()))->apply($template, $commands, $caughtExceptions);
// $this->exceptionHandler->handleAfterNodeCreation($caughtExceptions, $node);
} catch (TemplateNotCreatedException|TemplatePartiallyCreatedException $templateCreationException) {
throw $templateCreationException;
Expand Down
Loading

0 comments on commit 5c916d4

Please sign in to comment.