Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TASK: Adjust to split up references and properties #3668

Merged
merged 8 commits into from
Apr 6, 2024
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
Loading