Skip to content

Commit

Permalink
Merge pull request #3668 from neos/separateReferencesAndProperties
Browse files Browse the repository at this point in the history
TASK: Adjust to split up references and properties
  • Loading branch information
mhsdesign authored Apr 6, 2024
2 parents 73a4759 + 3266433 commit a5a4f6b
Show file tree
Hide file tree
Showing 7 changed files with 258 additions and 122 deletions.
42 changes: 19 additions & 23 deletions Classes/Domain/Model/Changes/Property.php
Original file line number Diff line number Diff line change
Expand Up @@ -134,9 +134,8 @@ public function canApply(): bool
}
$nodeType = $this->subject->nodeType;
$propertyName = $this->getPropertyName();
$nodeTypeProperties = $nodeType->getProperties();

return isset($nodeTypeProperties[$propertyName]);
return $nodeType->hasProperty($propertyName) || $nodeType->hasReference($propertyName);
}

/**
Expand All @@ -153,8 +152,6 @@ public function apply(): void
if ($this->canApply() && !is_null($subject) && !is_null($propertyName)) {
$contentRepository = $this->contentRepositoryRegistry->get($subject->subgraphIdentity->contentRepositoryId);

$propertyType = $this->getNodeType($subject)->getPropertyType($propertyName);

$workspace = $this->contentRepositoryRegistry->get($this->subject->subgraphIdentity->contentRepositoryId)
->getWorkspaceFinder()->findOneByCurrentContentStreamId($subject->subgraphIdentity->contentStreamId);
if (!$workspace) {
Expand All @@ -164,24 +161,16 @@ public function apply(): void
);
}

// Use extra commands for reference handling
if ($propertyType === 'reference' || $propertyType === 'references') {
if ($this->getNodeType($subject)->hasReference($propertyName)) {
// Use extra commands for reference handling
$value = $this->getValue();

$destinationNodeAggregateIds = [];
if ($propertyType === 'reference') {
if (is_string($value) && !empty($value)) {
$destinationNodeAggregateIds[] = $value;
}
}

if ($propertyType === 'references') {
/** @var array<int,string> $values */
$values = $value;
if (is_array($values)) {
foreach ($values as $singleNodeAggregateId) {
$destinationNodeAggregateIds[] = $singleNodeAggregateId;
}
}
if (is_string($value) && !empty($value)) {
$destinationNodeAggregateIds = [$value];
} elseif (is_array($value)) {
$destinationNodeAggregateIds = $value;
}

$commandResult = $contentRepository->handle(
Expand Down Expand Up @@ -286,8 +275,12 @@ public function apply(): void
$updateNodeInfo->setNode($node);
$this->feedbackCollection->add($updateNodeInfo);

$reloadIfChangedConfigurationPath = sprintf('properties.%s.ui.reloadIfChanged', $propertyName);
if (!$this->getIsInline() && $this->getNodeType($node)->getConfiguration($reloadIfChangedConfigurationPath)) {
if (!$this->getIsInline()
&& (
$this->getNodeType($node)->hasConfiguration(sprintf('properties.%s.ui.reloadIfChanged', $propertyName))
|| $this->getNodeType($node)->hasConfiguration(sprintf('references.%s.ui.reloadIfChanged', $propertyName))
)
) {
if ($this->getNodeDomAddress() && $this->getNodeDomAddress()->getFusionPath()
&& $parentNode
&& $this->getNodeType($parentNode)->isOfType('Neos.Neos:ContentCollection')) {
Expand All @@ -300,9 +293,12 @@ public function apply(): void
}
}

$reloadPageIfChangedConfigurationPath = sprintf('properties.%s.ui.reloadPageIfChanged', $propertyName);
if (!$this->getIsInline()
&& $this->getNodeType($node)->getConfiguration($reloadPageIfChangedConfigurationPath)) {
&& (
$this->getNodeType($node)->getConfiguration(sprintf('properties.%s.ui.reloadPageIfChanged', $propertyName))
|| $this->getNodeType($node)->getConfiguration(sprintf('references.%s.ui.reloadPageIfChanged', $propertyName))
)
) {
$this->reloadDocument($node);
}
}
Expand Down
4 changes: 4 additions & 0 deletions Classes/Domain/NodeCreation/NodeCreationElements.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@
* type: string
* ui:
* showInCreationDialog: true
* references:
* myReferences:
* ui:
* showInCreationDialog: true
*
* @implements \IteratorAggregate<string, mixed>
* @internal Especially the constructor and the serialized data
Expand Down
2 changes: 0 additions & 2 deletions Classes/Domain/Service/NodePropertyConversionService.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,6 @@ public function convert(string $propertyType, string|array|null $rawValue): mixe
return $rawValue;

case 'reference':
throw new \Exception("not implemented here, must be handled outside.");

case 'references':
throw new \Exception("not implemented here, must be handled outside.");

Expand Down
88 changes: 36 additions & 52 deletions Classes/Domain/Service/NodePropertyConverterService.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
use Neos\ContentRepository\Core\NodeType\NodeType;
use Neos\ContentRepository\Core\Projection\ContentGraph\Filter\FindReferencesFilter;
use Neos\ContentRepository\Core\Projection\ContentGraph\Node;
use Neos\ContentRepository\Core\Projection\ContentGraph\References;
use Neos\ContentRepositoryRegistry\ContentRepositoryRegistry;
use Neos\Flow\Annotations as Flow;
use Neos\Flow\Log\ThrowableStorageInterface;
Expand Down Expand Up @@ -101,49 +100,51 @@ public function injectThrowableStorage(ThrowableStorageInterface $throwableStora
}

/**
* Get a single property reduced to a simple type (no objects) representation
*
* @param Node $node
* @param string $propertyName
* @return mixed
* @return list<string>|string|null
*/
public function getProperty(Node $node, $propertyName)
private function getReference(Node $node, string $referenceName): array|string|null
{
if ($propertyName === '_hidden') {
return $node->tags->contain(SubtreeTag::fromString('disabled'));
$subgraph = $this->contentRepositoryRegistry->subgraphForNode($node);
$references = $subgraph->findReferences(
$node->nodeAggregateId,
FindReferencesFilter::create(referenceName: $referenceName)
);

$referenceIdentifiers = [];
foreach ($references as $reference) {
$referenceIdentifiers[] = $reference->node->nodeAggregateId->value;
}
$propertyType = $this->getNodeType($node)->getPropertyType($propertyName);

// We handle "reference" and "references" differently than other properties;
// because we need to use another API for querying these references.
if ($propertyType === 'reference') {
$subgraph = $this->contentRepositoryRegistry->subgraphForNode($node);
$referenceIdentifiers = $this->toNodeIdentifierStrings(
$subgraph->findReferences(
$node->nodeAggregateId,
FindReferencesFilter::create(referenceName: $propertyName)
)
);
$maxItems = $this->getNodeType($node)->getReferences()[$referenceName]['constraints']['maxItems'] ?? null;

if ($maxItems === 1) {
// special handling to simulate old single reference behaviour.
// todo should be adjusted in the ui
if (count($referenceIdentifiers) === 0) {
return null;
} else {
return reset($referenceIdentifiers);
}
} elseif ($propertyType === 'references') {
$subgraph = $this->contentRepositoryRegistry->subgraphForNode($node);
$references = $subgraph->findReferences(
$node->nodeAggregateId,
FindReferencesFilter::create(referenceName: $propertyName)
);
}
return $referenceIdentifiers;
}

return $this->toNodeIdentifierStrings($references);
// Here, the normal property access logic starts.
} elseif ($propertyName[0] === '_' && $propertyName !== '_hiddenInIndex') {
/**
* Get a single property reduced to a simple type (no objects) representation
*/
private function getProperty(Node $node, string $propertyName): mixed
{
if ($propertyName === '_hidden') {
return $node->tags->contain(SubtreeTag::fromString('disabled'));
}

if ($propertyName[0] === '_' && $propertyName !== '_hiddenInIndex') {
$propertyValue = ObjectAccess::getProperty($node, ltrim($propertyName, '_'));
} else {
$propertyValue = $node->getProperty($propertyName);
}

$propertyType = $this->getNodeType($node)->getPropertyType($propertyName);
try {
$convertedValue = $this->convertValue($propertyValue, $propertyType);
} catch (PropertyException $exception) {
Expand All @@ -169,35 +170,25 @@ public function getProperty(Node $node, $propertyName)
}

/**
* @return array<int,string>
*/
private function toNodeIdentifierStrings(References $references): array
{
$identifiers = [];
foreach ($references as $reference) {
$identifiers[] = $reference->node->nodeAggregateId->value;
}
return $identifiers;
}

/**
* Get all properties reduced to simple type (no objects) representations in an array
* Get all properties and references stuff reduced to simple type (no objects) representations in an array
*
* @param Node $node
* @return array<string,mixed>
*/
public function getPropertiesArray(Node $node)
{
$properties = [];
foreach ($this->getNodeType($node)->getProperties() as $propertyName => $propertyConfiguration) {
foreach ($this->getNodeType($node)->getProperties() as $propertyName => $_) {
if ($propertyName[0] === '_' && $propertyName[1] === '_') {
// skip fully-private properties
continue;
}

$properties[$propertyName] = $this->getProperty($node, $propertyName);
}

foreach ($this->getNodeType($node)->getReferences() as $referenceName => $_) {
$properties[$referenceName] = $this->getReference($node, $referenceName);
}
return $properties;
}

Expand Down Expand Up @@ -280,14 +271,7 @@ protected function createConfiguration($dataType)
$propertyMappingConfiguration = new PropertyMappingConfiguration();
$propertyMappingConfiguration->allowAllProperties();

$parsedType = [
'elementType' => null,
'type' => $dataType
];
// Special handling for "reference(s)", should be deprecated and normlized to array<Node>
if ($dataType !== 'references' && $dataType !== 'reference') {
$parsedType = TypeHandling::parseType($dataType);
}
$parsedType = TypeHandling::parseType($dataType);

if ($this->setTypeConverterForType($propertyMappingConfiguration, $dataType) === false) {
$this->setTypeConverterForType($propertyMappingConfiguration, $parsedType['type']);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
use Neos\ContentRepository\Core\NodeType\NodeType;
use Neos\ContentRepository\Core\NodeType\NodeTypePostprocessorInterface;
use Neos\Flow\Annotations as Flow;
use Neos\Neos\Ui\Domain\NodeCreation\NodeCreationElements;
use Neos\Utility\Arrays;
use Neos\Utility\PositionalArraySorter;

Expand Down Expand Up @@ -88,10 +89,14 @@ public function process(NodeType $nodeType, array &$configuration, array $option
{
$creationDialogElements = $configuration['ui']['creationDialog']['elements'] ?? [];

if (!empty($configuration['properties'] ?? null)) {
if (!empty($configuration['properties'])) {
$creationDialogElements = $this->promotePropertiesIntoCreationDialog($configuration['properties'], $creationDialogElements);
}

if (!empty($configuration['references'])) {
$creationDialogElements = $this->promoteReferencesIntoCreationDialog($configuration['references'], $creationDialogElements);
}

$this->mergeDefaultCreationDialogElementEditors($creationDialogElements);

if ($creationDialogElements !== []) {
Expand Down Expand Up @@ -224,4 +229,74 @@ private function promotePropertyIntoCreationDialog(string $propertyName, array $
$convertedConfiguration['ui']['editorOptions'] = $editorOptions;
return $convertedConfiguration;
}

/**
* @param array<string, mixed> $references
* @param array<string, mixed> $explicitCreationDialogElements
* @return array<string, mixed>
*/
private function promoteReferencesIntoCreationDialog(array $references, array $explicitCreationDialogElements): array
{
foreach ($references as $referenceName => $referenceConfiguration) {
if (
!isset($referenceConfiguration['ui']['showInCreationDialog'])
|| $referenceConfiguration['ui']['showInCreationDialog'] !== true
) {
continue;
}

$creationDialogElement = $this->promoteReferenceIntoCreationDialog($referenceConfiguration);
if (isset($explicitCreationDialogElements[$referenceName])) {
$creationDialogElement = Arrays::arrayMergeRecursiveOverrule(
$creationDialogElement,
$explicitCreationDialogElements[$referenceName]
);
}
$explicitCreationDialogElements[$referenceName] = $creationDialogElement;
}
return $explicitCreationDialogElements;
}

/**
* Converts a NodeType reference configuration to the corresponding creationDialog "element" configuration
*
* @param array<string,mixed> $referenceConfiguration
* @return array<string,mixed>
*/
private function promoteReferenceIntoCreationDialog(array $referenceConfiguration): array
{
$maxAllowedItems = $referenceConfiguration['constraints']['maxItems'] ?? null;
$referenceMagicType = $maxAllowedItems === 1 ? 'reference' : 'references';
/**
* For references, we add this magic type to the elements configuration {@see NodeCreationElements}
*/
$convertedConfiguration = [
'type' => $referenceMagicType,
];
if (isset($referenceConfiguration['ui']['label'])) {
$convertedConfiguration['ui']['label'] = $referenceConfiguration['ui']['label'];
}
if (isset($referenceConfiguration['defaultValue'])) {
$convertedConfiguration['defaultValue'] = $referenceConfiguration['defaultValue'];
}
if (isset($referenceConfiguration['ui']['help'])) {
$convertedConfiguration['ui']['help'] = $referenceConfiguration['ui']['help'];
}
if (isset($referenceConfiguration['ui']['inspector']['position'])) {
$convertedConfiguration['position'] = $referenceConfiguration['ui']['inspector']['position'];
}
if (isset($referenceConfiguration['ui']['inspector']['hidden'])) {
$convertedConfiguration['ui']['hidden'] = $referenceConfiguration['ui']['inspector']['hidden'];
}
/**
* the editor will be set based on the type above via {@see self::mergeDefaultCreationDialogElementEditors}
*/
if (isset($referenceConfiguration['ui']['inspector']['editor'])) {
$convertedConfiguration['ui']['editor'] = $referenceConfiguration['ui']['inspector']['editor'];
}
if (isset($referenceConfiguration['ui']['inspector']['editorOptions'])) {
$convertedConfiguration['ui']['editorOptions'] = $referenceConfiguration['ui']['inspector']['editorOptions'];
}
return $convertedConfiguration;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,7 @@ public function handle(NodeCreationCommands $commands, NodeCreationElements $ele
$setReferencesCommands = [];
foreach ($elements as $elementName => $elementValue) {
// handle properties
// todo this will be simplified once hasProperty does not return true for references
if ($nodeType->hasProperty($elementName) && ($nodeType->getPropertyType($elementName) !== 'references' && $nodeType->getPropertyType($elementName) !== 'reference')) {
if ($nodeType->hasProperty($elementName)) {
$propertyConfiguration = $nodeType->getProperties()[$elementName];
if (
($propertyConfiguration['ui']['showInCreationDialog'] ?? false) === true
Expand All @@ -52,10 +51,9 @@ public function handle(NodeCreationCommands $commands, NodeCreationElements $ele
}

// handle references
// todo this will be replaced by $nodeType->hasReference()
if ($nodeType->hasProperty($elementName) && ($nodeType->getPropertyType($elementName) === 'references' || $nodeType->getPropertyType($elementName) === 'reference')) {
if ($nodeType->hasReference($elementName)) {
assert($elementValue instanceof NodeAggregateIds);
$referenceConfiguration = $nodeType->getProperties()[$elementName];
$referenceConfiguration = $nodeType->getReferences()[$elementName];
if (
($referenceConfiguration['ui']['showInCreationDialog'] ?? false) === true
) {
Expand Down
Loading

0 comments on commit a5a4f6b

Please sign in to comment.