From 8dd7d356dc3b0c04770b9cf85917a0fa86400923 Mon Sep 17 00:00:00 2001 From: Bernhard Schmitt Date: Wed, 1 Nov 2023 16:09:22 +0100 Subject: [PATCH 01/23] 4549 - separate properties from references in nodetype declaration --- ...SetNodeReferences_ConstraintChecks.feature | 121 +++++++++--------- ...etNodeReferences_WithoutDimensions.feature | 83 ++++++------ ...3-SetNodeReferences_WithDimensions.feature | 17 ++- ...4-SetNodeReferences_PropertyScopes.feature | 14 +- ...odeVariation_After_NodeReferencing.feature | 71 +++++----- ...bleNodeAggregate_WithoutDimensions.feature | 5 +- ...isableNodeAggregate_WithDimensions.feature | 5 +- ...bleNodeAggregate_WithoutDimensions.feature | 5 +- ...EnableNodeAggregate_WithDimensions.feature | 5 +- ...oveNodeAggregate_WithoutDimensions.feature | 5 +- ...RemoveNodeAggregate_WithDimensions.feature | 5 +- .../NodeReferencesOnForkContentStream.feature | 25 ++-- .../AllCommandsAreImplemented.feature | 20 +-- .../RemoveNodeAggregateAfterDisabling.feature | 5 +- ...dChildNodeConnectedThroughEdgeName.feature | 3 +- .../NodeTraversal/FindNodeById.feature | 3 +- .../NodeTraversal/FindNodeByPath.feature | 3 +- .../NodeTraversal/FindParentNode.feature | 3 +- .../Features/NodeTraversal/References.feature | 3 +- .../NodeTraversal/SiblingNodes.feature | 3 +- .../Features/NodeTraversal/Timestamps.feature | 3 +- .../Feature/Common/ConstraintChecks.php | 26 ++-- .../NodeReferencing/NodeReferencing.php | 3 +- .../Property/PropertyConverter.php | 4 +- .../Classes/NodeType/NodeType.php | 15 +++ ...ricCommandExecutionAndEventPublication.php | 5 +- 26 files changed, 227 insertions(+), 233 deletions(-) diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/05-NodeReferencing/01-SetNodeReferences_ConstraintChecks.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/05-NodeReferencing/01-SetNodeReferences_ConstraintChecks.feature index 2e04fc4c051..dded08fe2ec 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/05-NodeReferencing/01-SetNodeReferences_ConstraintChecks.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/05-NodeReferencing/01-SetNodeReferences_ConstraintChecks.feature @@ -11,19 +11,16 @@ Feature: Constraint checks on SetNodeReferences """yaml 'Neos.ContentRepository.Testing:NodeWithReferences': properties: - referenceProperty: - type: reference - referencesProperty: - type: references nonReferenceProperty: type: string - constrainedReferenceProperty: - type: reference + references: + reference: {} + references: {} + constrainedReference: constraints: nodeTypes: 'Neos.ContentRepository.Testing:NodeWithReferences': false - referencePropertyWithProperties: - type: reference + referenceWithProperties: properties: text: type: string @@ -34,143 +31,143 @@ Feature: Constraint checks on SetNodeReferences And I am in content repository "default" And I am user identified by "initiating-user-identifier" And the command CreateRootWorkspace is executed with payload: - | Key | Value | - | workspaceName | "live" | - | workspaceTitle | "Live" | - | workspaceDescription | "The live workspace" | - | newContentStreamId | "cs-identifier" | + | Key | Value | + | workspaceName | "live" | + | workspaceTitle | "Live" | + | workspaceDescription | "The live workspace" | + | newContentStreamId | "cs-identifier" | And the graph projection is fully up to date And I am in content stream "cs-identifier" and dimension space point {"language":"de"} And the command CreateRootNodeAggregateWithNode is executed with payload: - | Key | Value | + | Key | Value | | nodeAggregateId | "lady-eleonode-rootford" | - | nodeTypeName | "Neos.ContentRepository:Root" | + | nodeTypeName | "Neos.ContentRepository:Root" | And the graph projection is fully up to date And the following CreateNodeAggregateWithNode commands are executed: - | nodeAggregateId | nodeTypeName | parentNodeAggregateId | - | source-nodandaise | Neos.ContentRepository.Testing:NodeWithReferences | lady-eleonode-rootford | - | anthony-destinode | Neos.ContentRepository.Testing:NodeWithReferences | lady-eleonode-rootford | - | berta-destinode | Neos.ContentRepository.Testing:NodeWithReferences | lady-eleonode-rootford | + | nodeAggregateId | nodeTypeName | parentNodeAggregateId | + | source-nodandaise | Neos.ContentRepository.Testing:NodeWithReferences | lady-eleonode-rootford | + | anthony-destinode | Neos.ContentRepository.Testing:NodeWithReferences | lady-eleonode-rootford | + | berta-destinode | Neos.ContentRepository.Testing:NodeWithReferences | lady-eleonode-rootford | # checks for contentStreamId Scenario: Try to reference nodes in a non-existent content stream When the command SetNodeReferences is executed with payload and exceptions are caught: - | Key | Value | - | contentStreamId | "i-do-not-exist" | - | sourceNodeAggregateId | "source-nodandaise" | - | referenceName | "referenceProperty" | - | references | [{"target":"anthony-destinode"}] | + | Key | Value | + | contentStreamId | "i-do-not-exist" | + | sourceNodeAggregateId | "source-nodandaise" | + | referenceName | "reference" | + | references | [{"target":"anthony-destinode"}] | Then the last command should have thrown an exception of type "ContentStreamDoesNotExistYet" with code 1521386692 # checks for sourceNodeAggregateId Scenario: Try to reference nodes in a non-existent node aggregate When the command SetNodeReferences is executed with payload and exceptions are caught: - | Key | Value | + | Key | Value | | sourceNodeAggregateId | "i-do-not-exist" | - | referenceName | "referenceProperty" | - | references | [{"target":"anthony-destinode"}] | + | referenceName | "reference" | + | references | [{"target":"anthony-destinode"}] | Then the last command should have thrown an exception of type "NodeAggregateCurrentlyDoesNotExist" with code 1541678486 Scenario: Try to reference nodes in a root node aggregate When the command SetNodeReferences is executed with payload and exceptions are caught: - | Key | Value | + | Key | Value | | sourceNodeAggregateId | "lady-eleonode-rootford" | - | referenceName | "referenceProperty" | - | references | [{"target":"anthony-destinode"}] | + | referenceName | "reference" | + | references | [{"target":"anthony-destinode"}] | Then the last command should have thrown an exception of type "NodeAggregateIsRoot" # checks for sourceOriginDimensionSpacePoint Scenario: Try to reference nodes in an origin dimension space point that does not exist When the command SetNodeReferences is executed with payload and exceptions are caught: | Key | Value | - | sourceNodeAggregateId | "source-nodandaise" | + | sourceNodeAggregateId | "source-nodandaise" | | sourceOriginDimensionSpacePoint | {"undeclared":"undefined"} | - | referenceName | "referenceProperty" | + | referenceName | "reference" | | references | [{"target":"anthony-destinode"}] | Then the last command should have thrown an exception of type "DimensionSpacePointNotFound" with code 1505929456 Scenario: Try to reference nodes in an origin dimension space point the source node aggregate does not occupy When the command SetNodeReferences is executed with payload and exceptions are caught: | Key | Value | - | sourceNodeAggregateId | "source-nodandaise" | + | sourceNodeAggregateId | "source-nodandaise" | | sourceOriginDimensionSpacePoint | {"language":"en"} | - | referenceName | "referenceProperty" | + | referenceName | "reference" | | references | [{"target":"anthony-destinode"}] | Then the last command should have thrown an exception of type "DimensionSpacePointIsNotYetOccupied" with code 1552595396 # checks for destinationnodeAggregateIds Scenario: Try to reference a non-existent node aggregate When the command SetNodeReferences is executed with payload and exceptions are caught: - | Key | Value | + | Key | Value | | sourceNodeAggregateId | "source-nodandaise" | - | referenceName | "referenceProperty" | - | references | [{"target":"i-do-not-exist"}] | + | referenceName | "reference" | + | references | [{"target":"i-do-not-exist"}] | Then the last command should have thrown an exception of type "NodeAggregateCurrentlyDoesNotExist" with code 1541678486 Scenario: Try to reference a root node aggregate When the command SetNodeReferences is executed with payload and exceptions are caught: - | Key | Value | + | Key | Value | | sourceNodeAggregateId | "source-nodandaise" | - | referenceName | "referenceProperty" | - | references | [{"target":"lady-eleonode-rootford"}] | + | referenceName | "reference" | + | references | [{"target":"lady-eleonode-rootford"}] | Then the last command should have thrown an exception of type "NodeAggregateIsRoot" Scenario: Try to reference a node aggregate of a type not matching the constraints When the command SetNodeReferences is executed with payload and exceptions are caught: - | Key | Value | + | Key | Value | | sourceNodeAggregateId | "source-nodandaise" | - | referenceName | "constrainedReferenceProperty" | - | references | [{"target":"anthony-destinode"}] | + | referenceName | "constrainedReference" | + | references | [{"target":"anthony-destinode"}] | Then the last command should have thrown an exception of type "ReferenceCannotBeSet" with code 1648502149 Scenario: Try to reference a node aggregate which does not cover the source origin Given the command CreateNodeAggregateWithNode is executed with payload: - | Key | Value | - | nodeAggregateId | "sir-david-nodenborough" | - | nodeTypeName | "Neos.ContentRepository.Testing:NodeWithReferences" | - | originDimensionSpacePoint | {"language":"en"} | - | parentNodeAggregateId | "lady-eleonode-rootford" | + | Key | Value | + | nodeAggregateId | "sir-david-nodenborough" | + | nodeTypeName | "Neos.ContentRepository.Testing:NodeWithReferences" | + | originDimensionSpacePoint | {"language":"en"} | + | parentNodeAggregateId | "lady-eleonode-rootford" | And the graph projection is fully up to date When the command SetNodeReferences is executed with payload and exceptions are caught: | Key | Value | - | sourceNodeAggregateId | "source-nodandaise" | + | sourceNodeAggregateId | "source-nodandaise" | | sourceOriginDimensionSpacePoint | {"language": "de"} | - | referenceName | "referenceProperty" | + | referenceName | "reference" | | references | [{"target":"sir-david-nodenborough"}] | Then the last command should have thrown an exception of type "NodeAggregateDoesCurrentlyNotCoverDimensionSpacePoint" # checks for referenceName Scenario: Try to reference nodes in an undefined property: When the command SetNodeReferences is executed with payload and exceptions are caught: - | Key | Value | + | Key | Value | | sourceNodeAggregateId | "source-nodandaise" | - | referenceName | "i-do-not-exist" | - | references | [{"target":"anthony-destinode"}] | + | referenceName | "i-do-not-exist" | + | references | [{"target":"anthony-destinode"}] | Then the last command should have thrown an exception of type "ReferenceCannotBeSet" with code 1618670106 Scenario: Try to reference nodes in a property that is not of type reference(s): When the command SetNodeReferences is executed with payload and exceptions are caught: - | Key | Value | + | Key | Value | | sourceNodeAggregateId | "source-nodandaise" | - | referenceName | "nonReferenceProperty" | - | references | [{"target":"anthony-destinode"}] | + | referenceName | "nonReferenceProperty" | + | references | [{"target":"anthony-destinode"}] | Then the last command should have thrown an exception of type "ReferenceCannotBeSet" with code 1618670106 Scenario: Try to reference a node aggregate using a property the reference does not declare When the command SetNodeReferences is executed with payload and exceptions are caught: - | Key | Value | + | Key | Value | | nodeAggregateId | "nody-mc-nodeface" | | sourceNodeAggregateId | "source-nodandaise" | - | referenceName | "referencePropertyWithProperties" | - | references | [{"target":"anthony-destinode", "properties":{"i-do-not-exist": "whatever"}}] | + | referenceName | "referenceWithProperties" | + | references | [{"target":"anthony-destinode", "properties":{"i-do-not-exist": "whatever"}}] | Then the last command should have thrown an exception of type "ReferenceCannotBeSet" with code 1658406662 Scenario: Try to set a property with a value of a wrong type When the command SetNodeReferences is executed with payload and exceptions are caught: - | Key | Value | + | Key | Value | | nodeAggregateId | "nody-mc-nodeface" | | sourceNodeAggregateId | "source-nodandaise" | - | referenceName | "referencePropertyWithProperties" | - | references | [{"target":"anthony-destinode", "properties":{"postalAddress": "28 31st of February Street"}}] | + | referenceName | "referenceWithProperties" | + | references | [{"target":"anthony-destinode", "properties":{"postalAddress": "28 31st of February Street"}}] | Then the last command should have thrown an exception of type "ReferenceCannotBeSet" with code 1658406762 diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/05-NodeReferencing/02-SetNodeReferences_WithoutDimensions.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/05-NodeReferencing/02-SetNodeReferences_WithoutDimensions.feature index fdd2769f2f5..f5729b00e6b 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/05-NodeReferencing/02-SetNodeReferences_WithoutDimensions.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/05-NodeReferencing/02-SetNodeReferences_WithoutDimensions.feature @@ -8,19 +8,15 @@ Feature: Node References without Dimensions And using the following node types: """yaml 'Neos.ContentRepository.Testing:NodeWithReferences': - properties: - referenceProperty: - type: reference - referencesProperty: - type: references - restrictedReferenceProperty: - type: reference + references: + reference: {} + references: {} + restrictedReference: constraints: nodeTypes: '*': false 'Neos.ContentRepository.Testing:NodeWithReferences': true - referencePropertyWithProperty: - type: reference + referenceWithProperty: properties: text: type: string @@ -28,8 +24,7 @@ Feature: Node References without Dimensions type: Neos\ContentRepository\Core\Tests\Behavior\Fixtures\DayOfWeek postalAddress: type: 'Neos\ContentRepository\Core\Tests\Behavior\Fixtures\PostalAddress' - referencesPropertyWithProperty: - type: references + referencesWithProperty: properties: text: type: string @@ -65,110 +60,110 @@ Feature: Node References without Dimensions When the command SetNodeReferences is executed with payload: | Key | Value | | sourceNodeAggregateId | "source-nodandaise" | - | referenceName | "referenceProperty" | + | referenceName | "reference" | | references | [{"target": "anthony-destinode"}] | And the graph projection is fully up to date Then I expect node aggregate identifier "source-nodandaise" to lead to node cs-identifier;source-nodandaise;{} And I expect this node to have the following references: | Name | Node | Properties | - | referenceProperty | cs-identifier;anthony-destinode;{} | null | + | reference | cs-identifier;anthony-destinode;{} | null | And I expect node aggregate identifier "anthony-destinode" to lead to node cs-identifier;anthony-destinode;{} And I expect this node to be referenced by: | Name | Node | Properties | - | referenceProperty | cs-identifier;source-nodandaise;{} | null | + | reference | cs-identifier;source-nodandaise;{} | null | Scenario: Ensure that a single reference with properties between nodes can be set and read When the command SetNodeReferences is executed with payload: | Key | Value | | sourceNodeAggregateId | "source-nodandaise" | - | referenceName | "referencePropertyWithProperty" | + | referenceName | "referenceWithProperty" | | references | [{"target": "anthony-destinode", "properties":{"text":"my text", "dayOfWeek":"DayOfWeek:https://schema.org/Friday", "postalAddress":"PostalAddress:dummy"}}] | And the graph projection is fully up to date Then I expect node aggregate identifier "source-nodandaise" to lead to node cs-identifier;source-nodandaise;{} And I expect this node to have the following references: | Name | Node | Properties | - | referencePropertyWithProperty | cs-identifier;anthony-destinode;{} | {"text":"my text", "dayOfWeek":"DayOfWeek:https://schema.org/Friday", "postalAddress":"PostalAddress:dummy"} | + | referenceWithProperty | cs-identifier;anthony-destinode;{} | {"text":"my text", "dayOfWeek":"DayOfWeek:https://schema.org/Friday", "postalAddress":"PostalAddress:dummy"} | And I expect node aggregate identifier "anthony-destinode" to lead to node cs-identifier;anthony-destinode;{} And I expect this node to be referenced by: | Name | Node | Properties | - | referencePropertyWithProperty | cs-identifier;source-nodandaise;{} | {"text":"my text", "dayOfWeek":"DayOfWeek:https://schema.org/Friday", "postalAddress":"PostalAddress:dummy"} | + | referenceWithProperty | cs-identifier;source-nodandaise;{} | {"text":"my text", "dayOfWeek":"DayOfWeek:https://schema.org/Friday", "postalAddress":"PostalAddress:dummy"} | Scenario: Ensure that multiple references between nodes can be set and read When the command SetNodeReferences is executed with payload: | Key | Value | | sourceNodeAggregateId | "source-nodandaise" | - | referenceName | "referencesProperty" | + | referenceName | "references" | | references | [{"target": "berta-destinode"}, {"target": "carl-destinode"}] | And the graph projection is fully up to date Then I expect node aggregate identifier "source-nodandaise" to lead to node cs-identifier;source-nodandaise;{} And I expect this node to have the following references: | Name | Node | Properties | - | referencesProperty | cs-identifier;berta-destinode;{} | null | - | referencesProperty | cs-identifier;carl-destinode;{} | null | + | references | cs-identifier;berta-destinode;{} | null | + | references | cs-identifier;carl-destinode;{} | null | And I expect node aggregate identifier "berta-destinode" to lead to node cs-identifier;berta-destinode;{} And I expect this node to be referenced by: | Name | Node | Properties | - | referencesProperty | cs-identifier;source-nodandaise;{} | null | + | references | cs-identifier;source-nodandaise;{} | null | And I expect node aggregate identifier "carl-destinode" to lead to node cs-identifier;carl-destinode;{} And I expect this node to be referenced by: | Name | Node | Properties | - | referencesProperty | cs-identifier;source-nodandaise;{} | null | + | references | cs-identifier;source-nodandaise;{} | null | Scenario: Ensure that multiple references with properties between nodes can be set and read When the command SetNodeReferences is executed with payload: | Key | Value | | sourceNodeAggregateId | "source-nodandaise" | - | referenceName | "referencesPropertyWithProperty" | + | referenceName | "referencesWithProperty" | | references | [{"target":"berta-destinode", "properties":{"text":"my text", "dayOfWeek":"DayOfWeek:https://schema.org/Wednesday", "postalAddress":"PostalAddress:dummy"}}, {"target":"carl-destinode", "properties":{"text":"my other text", "dayOfWeek":"DayOfWeek:https://schema.org/Friday", "postalAddress":"PostalAddress:anotherDummy"}}] | And the graph projection is fully up to date Then I expect node aggregate identifier "source-nodandaise" to lead to node cs-identifier;source-nodandaise;{} And I expect this node to have the following references: | Name | Node | Properties | - | referencesPropertyWithProperty | cs-identifier;berta-destinode;{} | {"text":"my text", "dayOfWeek":"DayOfWeek:https://schema.org/Wednesday", "postalAddress":"PostalAddress:dummy"} | - | referencesPropertyWithProperty | cs-identifier;carl-destinode;{} | {"text":"my other text", "dayOfWeek":"DayOfWeek:https://schema.org/Friday", "postalAddress":"PostalAddress:anotherDummy"} | + | referencesWithProperty | cs-identifier;berta-destinode;{} | {"text":"my text", "dayOfWeek":"DayOfWeek:https://schema.org/Wednesday", "postalAddress":"PostalAddress:dummy"} | + | referencesWithProperty | cs-identifier;carl-destinode;{} | {"text":"my other text", "dayOfWeek":"DayOfWeek:https://schema.org/Friday", "postalAddress":"PostalAddress:anotherDummy"} | And I expect node aggregate identifier "berta-destinode" to lead to node cs-identifier;berta-destinode;{} And I expect this node to be referenced by: | Name | Node | Properties | - | referencesPropertyWithProperty | cs-identifier;source-nodandaise;{} | {"text":"my text", "dayOfWeek":"DayOfWeek:https://schema.org/Wednesday", "postalAddress":"PostalAddress:dummy"} | + | referencesWithProperty | cs-identifier;source-nodandaise;{} | {"text":"my text", "dayOfWeek":"DayOfWeek:https://schema.org/Wednesday", "postalAddress":"PostalAddress:dummy"} | And I expect node aggregate identifier "carl-destinode" to lead to node cs-identifier;carl-destinode;{} And I expect this node to be referenced by: | Name | Node | Properties | - | referencesPropertyWithProperty | cs-identifier;source-nodandaise;{} | {"text":"my other text", "dayOfWeek":"DayOfWeek:https://schema.org/Friday", "postalAddress":"PostalAddress:anotherDummy"} | + | referencesWithProperty | cs-identifier;source-nodandaise;{} | {"text":"my other text", "dayOfWeek":"DayOfWeek:https://schema.org/Friday", "postalAddress":"PostalAddress:anotherDummy"} | Scenario: Ensure that references between nodes can be set and overwritten When the command SetNodeReferences is executed with payload: | Key | Value | | sourceNodeAggregateId | "source-nodandaise" | | references | [{"target": "berta-destinode"}, {"target": "carl-destinode"}] | - | referenceName | "referencesProperty" | + | referenceName | "references" | And the graph projection is fully up to date And the command SetNodeReferences is executed with payload: | Key | Value | | sourceNodeAggregateId | "source-nodandaise" | | references | [{"target": "anthony-destinode"}] | - | referenceName | "referencesProperty" | + | referenceName | "references" | And the graph projection is fully up to date Then I expect node aggregate identifier "source-nodandaise" to lead to node cs-identifier;source-nodandaise;{} And I expect this node to have the following references: | Name | Node | Properties | - | referencesProperty | cs-identifier;anthony-destinode;{} | null | + | references | cs-identifier;anthony-destinode;{} | null | And I expect node aggregate identifier "anthony-destinode" to lead to node cs-identifier;anthony-destinode;{} And I expect this node to be referenced by: | Name | Node | Properties | - | referencesProperty | cs-identifier;source-nodandaise;{} | null | + | references | cs-identifier;source-nodandaise;{} | null | And I expect node aggregate identifier "berta-destinode" to lead to node cs-identifier;berta-destinode;{} And I expect this node to not be referenced @@ -182,21 +177,21 @@ Feature: Node References without Dimensions | Key | Value | | sourceNodeAggregateId | "source-nodandaise" | | references | [{"target": "berta-destinode"}, {"target": "carl-destinode"}] | - | referenceName | "referencesProperty" | + | referenceName | "references" | And the graph projection is fully up to date And the command SetNodeReferences is executed with payload: | Key | Value | | sourceNodeAggregateId | "source-nodandaise" | | references | [{"target": "carl-destinode"}, {"target": "berta-destinode"}] | - | referenceName | "referencesProperty" | + | referenceName | "references" | And the graph projection is fully up to date Then I expect node aggregate identifier "source-nodandaise" to lead to node cs-identifier;source-nodandaise;{} And I expect this node to have the following references: | Name | Node | Properties | - | referencesProperty | cs-identifier;carl-destinode;{} | null | - | referencesProperty | cs-identifier;berta-destinode;{} | null | + | references | cs-identifier;carl-destinode;{} | null | + | references | cs-identifier;berta-destinode;{} | null | Scenario: Ensure that references between nodes can be deleted @@ -204,14 +199,14 @@ Feature: Node References without Dimensions | Key | Value | | sourceNodeAggregateId | "source-nodandaise" | | references | [{"target": "berta-destinode"}, {"target": "carl-destinode"}] | - | referenceName | "referencesProperty" | + | referenceName | "references" | And the graph projection is fully up to date And the command SetNodeReferences is executed with payload: | Key | Value | | sourceNodeAggregateId | "source-nodandaise" | | references | [] | - | referenceName | "referencesProperty" | + | referenceName | "references" | And the graph projection is fully up to date @@ -230,36 +225,36 @@ Feature: Node References without Dimensions | Key | Value | | sourceNodeAggregateId | "source-nodandaise" | | references | [{"target": "anthony-destinode"}] | - | referenceName | "referenceProperty" | + | referenceName | "reference" | And the graph projection is fully up to date And the command SetNodeReferences is executed with payload: | Key | Value | | sourceNodeAggregateId | "berta-destinode" | | references | [{"target": "anthony-destinode"}] | - | referenceName | "referenceProperty" | + | referenceName | "reference" | And the graph projection is fully up to date Then I expect node aggregate identifier "anthony-destinode" to lead to node cs-identifier;anthony-destinode;{} And I expect this node to be referenced by: | Name | Node | Properties | - | referenceProperty | cs-identifier;berta-destinode;{} | null | - | referenceProperty | cs-identifier;source-nodandaise;{} | null | + | reference | cs-identifier;berta-destinode;{} | null | + | reference | cs-identifier;source-nodandaise;{} | null | Scenario: Ensure that a reference between nodes can be set and read when matching constraints When the command SetNodeReferences is executed with payload: | Key | Value | | sourceNodeAggregateId | "source-nodandaise" | | references | [{"target": "anthony-destinode"}] | - | referenceName | "restrictedReferenceProperty" | + | referenceName | "restrictedReference" | And the graph projection is fully up to date Then I expect node aggregate identifier "source-nodandaise" to lead to node cs-identifier;source-nodandaise;{} And I expect this node to have the following references: | Name | Node | Properties | - | restrictedReferenceProperty | cs-identifier;anthony-destinode;{} | null | + | restrictedReference | cs-identifier;anthony-destinode;{} | null | And I expect node aggregate identifier "anthony-destinode" to lead to node cs-identifier;anthony-destinode;{} And I expect this node to be referenced by: | Name | Node | Properties | - | restrictedReferenceProperty | cs-identifier;source-nodandaise;{} | null | + | restrictedReference | cs-identifier;source-nodandaise;{} | null | diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/05-NodeReferencing/03-SetNodeReferences_WithDimensions.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/05-NodeReferencing/03-SetNodeReferences_WithDimensions.feature index ce9a9f8f4f6..bd7620a20b2 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/05-NodeReferencing/03-SetNodeReferences_WithDimensions.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/05-NodeReferencing/03-SetNodeReferences_WithDimensions.feature @@ -13,12 +13,11 @@ Feature: Node References with Dimensions """yaml 'Neos.ContentRepository.Testing:NodeWithReferences': properties: - referenceProperty: - type: reference - referencesProperty: - type: references text: type: string + references: + reference: {} + references: {} """ And using identifier "default", I define a content repository And I am in content repository "default" @@ -45,7 +44,7 @@ Feature: Node References with Dimensions When the command SetNodeReferences is executed with payload: | Key | Value | | sourceNodeAggregateId | "source-nodandaise" | - | referenceName | "referenceProperty" | + | referenceName | "reference" | | references | [{"target": "anthony-destinode"}] | And the graph projection is fully up to date @@ -53,18 +52,18 @@ Feature: Node References with Dimensions Then I expect node aggregate identifier "source-nodandaise" to lead to node cs-identifier;source-nodandaise;{"language": "de"} And I expect this node to have the following references: | Name | Node | Properties | - | referenceProperty | cs-identifier;anthony-destinode;{"language": "de"} | null | + | reference | cs-identifier;anthony-destinode;{"language": "de"} | null | Then I expect node aggregate identifier "anthony-destinode" to lead to node cs-identifier;anthony-destinode;{"language": "de"} And I expect this node to be referenced by: | Name | Node | Properties | - | referenceProperty | cs-identifier;source-nodandaise;{"language": "de"} | null | + | reference | cs-identifier;source-nodandaise;{"language": "de"} | null | When I am in content stream "cs-identifier" and dimension space point {"language": "ch"} Then I expect node aggregate identifier "source-nodandaise" to lead to node cs-identifier;source-nodandaise;{"language": "de"} And I expect this node to have the following references: | Name | Node | Properties | - | referenceProperty | cs-identifier;anthony-destinode;{"language": "de"} | null | + | reference | cs-identifier;anthony-destinode;{"language": "de"} | null | Then I expect node aggregate identifier "anthony-destinode" to lead to node cs-identifier;anthony-destinode;{"language": "de"} And I expect this node to be referenced by: | Name | Node | Properties | - | referenceProperty | cs-identifier;source-nodandaise;{"language": "de"} | null | + | reference | cs-identifier;source-nodandaise;{"language": "de"} | null | diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/05-NodeReferencing/04-SetNodeReferences_PropertyScopes.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/05-NodeReferencing/04-SetNodeReferences_PropertyScopes.feature index b5be6986e06..7718b6bcdaa 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/05-NodeReferencing/04-SetNodeReferences_PropertyScopes.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/05-NodeReferencing/04-SetNodeReferences_PropertyScopes.feature @@ -10,28 +10,20 @@ Feature: Set node properties with different scopes And using the following node types: """yaml 'Neos.ContentRepository.Testing:NodeWithReferences': - properties: - unscopedReference: - type: reference - unscopedReferences: - type: references + references: + unscopedReference: {} + unscopedReferences: {} nodeScopedReference: - type: reference scope: node nodeScopedReferences: - type: references scope: node nodeAggregateScopedReference: - type: reference scope: nodeAggregate nodeAggregateScopedReferences: - type: references scope: nodeAggregate specializationsScopedReference: - type: reference scope: specializations specializationsScopedReferences: - type: references scope: specializations """ And using identifier "default", I define a content repository diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/05-NodeReferencing/05-NodeVariation_After_NodeReferencing.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/05-NodeReferencing/05-NodeVariation_After_NodeReferencing.feature index ae2e995690a..da3381d70f7 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/05-NodeReferencing/05-NodeVariation_After_NodeReferencing.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/05-NodeReferencing/05-NodeVariation_After_NodeReferencing.feature @@ -13,12 +13,11 @@ Feature: Node References with Dimensions """yaml 'Neos.ContentRepository.Testing:NodeWithReferences': properties: - referenceProperty: - type: reference - referencesProperty: - type: references text: type: string + references: + reference: {} + references: {} """ And using identifier "default", I define a content repository And I am in content repository "default" @@ -45,7 +44,7 @@ Feature: Node References with Dimensions When the command SetNodeReferences is executed with payload: | Key | Value | | sourceNodeAggregateId | "source-nodandaise" | - | referenceName | "referenceProperty" | + | referenceName | "reference" | | references | [{"target": "anthony-destinode"}] | And the graph projection is fully up to date @@ -61,30 +60,30 @@ Feature: Node References with Dimensions Then I expect node aggregate identifier "source-nodandaise" to lead to node cs-identifier;source-nodandaise;{"language": "ch"} And I expect this node to have the following references: | Name | Node | Properties | - | referenceProperty | cs-identifier;anthony-destinode;{"language": "de"} | null | + | reference | cs-identifier;anthony-destinode;{"language": "de"} | null | Then I expect node aggregate identifier "anthony-destinode" to lead to node cs-identifier;anthony-destinode;{"language": "de"} And I expect this node to be referenced by: | Name | Node | Properties | - | referenceProperty | cs-identifier;source-nodandaise;{"language": "ch"} | null | + | reference | cs-identifier;source-nodandaise;{"language": "ch"} | null | # the reference must also exist on the non-touched nodes When I am in content stream "cs-identifier" and dimension space point {"language": "de"} Then I expect node aggregate identifier "source-nodandaise" to lead to node cs-identifier;source-nodandaise;{"language": "de"} And I expect this node to have the following references: | Name | Node | Properties | - | referenceProperty | cs-identifier;anthony-destinode;{"language": "de"} | null | + | reference | cs-identifier;anthony-destinode;{"language": "de"} | null | And I expect node aggregate identifier "anthony-destinode" to lead to node cs-identifier;anthony-destinode;{"language": "de"} And I expect this node to be referenced by: | Name | Node | Properties | - | referenceProperty | cs-identifier;source-nodandaise;{"language": "de"} | null | + | reference | cs-identifier;source-nodandaise;{"language": "de"} | null | # now, when modifying the specialization reference, only the specialization is changed. When the command SetNodeReferences is executed with payload: | Key | Value | | sourceNodeAggregateId | "source-nodandaise" | | sourceOriginDimensionSpacePoint | {"language": "ch"} | - | referenceName | "referenceProperty" | + | referenceName | "reference" | | references | [{"target": "source-nodandaise"}] | And the graph projection is fully up to date @@ -93,21 +92,21 @@ Feature: Node References with Dimensions Then I expect node aggregate identifier "source-nodandaise" to lead to node cs-identifier;source-nodandaise;{"language": "ch"} And I expect this node to have the following references: | Name | Node | Properties | - | referenceProperty | cs-identifier;source-nodandaise;{"language": "ch"} | null | + | reference | cs-identifier;source-nodandaise;{"language": "ch"} | null | And I expect this node to be referenced by: | Name | Node | Properties | - | referenceProperty | cs-identifier;source-nodandaise;{"language": "ch"} | null | + | reference | cs-identifier;source-nodandaise;{"language": "ch"} | null | # unmodified on the untouched nodes When I am in content stream "cs-identifier" and dimension space point {"language": "de"} Then I expect node aggregate identifier "source-nodandaise" to lead to node cs-identifier;source-nodandaise;{"language": "de"} And I expect this node to have the following references: | Name | Node | Properties | - | referenceProperty | cs-identifier;anthony-destinode;{"language": "de"} | null | + | reference | cs-identifier;anthony-destinode;{"language": "de"} | null | Then I expect node aggregate identifier "anthony-destinode" to lead to node cs-identifier;anthony-destinode;{"language": "de"} And I expect this node to be referenced by: | Name | Node | Properties | - | referenceProperty | cs-identifier;source-nodandaise;{"language": "de"} | null | + | reference | cs-identifier;source-nodandaise;{"language": "de"} | null | Scenario: specialize the source node, only set reference on the specialization. Then, the reference should only appear on the specialization When the command CreateNodeVariant is executed with payload: @@ -121,7 +120,7 @@ Feature: Node References with Dimensions | Key | Value | | sourceNodeAggregateId | "source-nodandaise" | | sourceOriginDimensionSpacePoint | {"language": "ch"} | - | referenceName | "referenceProperty" | + | referenceName | "reference" | | references | [{"target": "anthony-destinode"}] | And the graph projection is fully up to date @@ -131,11 +130,11 @@ Feature: Node References with Dimensions Then I expect node aggregate identifier "source-nodandaise" to lead to node cs-identifier;source-nodandaise;{"language": "ch"} And I expect this node to have the following references: | Name | Node | Properties | - | referenceProperty | cs-identifier;anthony-destinode;{"language": "de"} | null | + | reference | cs-identifier;anthony-destinode;{"language": "de"} | null | Then I expect node aggregate identifier "anthony-destinode" to lead to node cs-identifier;anthony-destinode;{"language": "de"} And I expect this node to be referenced by: | Name | Node | Properties | - | referenceProperty | cs-identifier;source-nodandaise;{"language": "ch"} | null | + | reference | cs-identifier;source-nodandaise;{"language": "ch"} | null | # on the other nodes, the reference does not exist. When I am in content stream "cs-identifier" and dimension space point {"language": "de"} @@ -158,7 +157,7 @@ Feature: Node References with Dimensions When the command SetNodeReferences is executed with payload: | Key | Value | | sourceNodeAggregateId | "source-nodandaise" | - | referenceName | "referenceProperty" | + | referenceName | "reference" | | references | [{"target": "anthony-destinode"}] | And the graph projection is fully up to date @@ -174,32 +173,32 @@ Feature: Node References with Dimensions Then I expect node aggregate identifier "source-nodandaise" to lead to node cs-identifier;source-nodandaise;{"language": "en"} And I expect this node to have the following references: | Name | Node | Properties | - | referenceProperty | cs-identifier;anthony-destinode;{"language": "en"} | null | + | reference | cs-identifier;anthony-destinode;{"language": "en"} | null | Then I expect node aggregate identifier "anthony-destinode" to lead to node cs-identifier;anthony-destinode;{"language": "en"} And I expect this node to be referenced by: | Name | Node | Properties | - | referenceProperty | cs-identifier;source-nodandaise;{"language": "en"} | null | + | reference | cs-identifier;source-nodandaise;{"language": "en"} | null | # the reference must also exist on the non-touched nodes When I am in content stream "cs-identifier" and dimension space point {"language": "de"} Then I expect node aggregate identifier "source-nodandaise" to lead to node cs-identifier;source-nodandaise;{"language": "de"} And I expect this node to have the following references: | Name | Node | Properties | - | referenceProperty | cs-identifier;anthony-destinode;{"language": "de"} | null | + | reference | cs-identifier;anthony-destinode;{"language": "de"} | null | Then I expect node aggregate identifier "anthony-destinode" to lead to node cs-identifier;anthony-destinode;{"language": "de"} And I expect this node to be referenced by: | Name | Node | Properties | - | referenceProperty | cs-identifier;source-nodandaise;{"language": "de"} | null | + | reference | cs-identifier;source-nodandaise;{"language": "de"} | null | When I am in content stream "cs-identifier" and dimension space point {"language": "ch"} Then I expect node aggregate identifier "source-nodandaise" to lead to node cs-identifier;source-nodandaise;{"language": "de"} And I expect this node to have the following references: | Name | Node | Properties | - | referenceProperty | cs-identifier;anthony-destinode;{"language": "de"} | null | + | reference | cs-identifier;anthony-destinode;{"language": "de"} | null | Then I expect node aggregate identifier "anthony-destinode" to lead to node cs-identifier;anthony-destinode;{"language": "de"} And I expect this node to be referenced by: | Name | Node | Properties | - | referenceProperty | cs-identifier;source-nodandaise;{"language": "de"} | null | + | reference | cs-identifier;source-nodandaise;{"language": "de"} | null | # now, when modifying the peer reference, only the peer is changed. @@ -207,7 +206,7 @@ Feature: Node References with Dimensions | Key | Value | | sourceNodeAggregateId | "source-nodandaise" | | sourceOriginDimensionSpacePoint | {"language": "en"} | - | referenceName | "referenceProperty" | + | referenceName | "reference" | | references | [{"target": "source-nodandaise"}] | And the graph projection is fully up to date @@ -216,31 +215,31 @@ Feature: Node References with Dimensions Then I expect node aggregate identifier "source-nodandaise" to lead to node cs-identifier;source-nodandaise;{"language": "en"} And I expect this node to have the following references: | Name | Node | Properties | - | referenceProperty | cs-identifier;source-nodandaise;{"language": "en"} | null | + | reference | cs-identifier;source-nodandaise;{"language": "en"} | null | And I expect this node to be referenced by: | Name | Node | Properties | - | referenceProperty | cs-identifier;source-nodandaise;{"language": "en"} | null | + | reference | cs-identifier;source-nodandaise;{"language": "en"} | null | # unmodified on the untouched nodes When I am in content stream "cs-identifier" and dimension space point {"language": "de"} Then I expect node aggregate identifier "source-nodandaise" to lead to node cs-identifier;source-nodandaise;{"language": "de"} And I expect this node to have the following references: | Name | Node | Properties | - | referenceProperty | cs-identifier;anthony-destinode;{"language": "de"} | null | + | reference | cs-identifier;anthony-destinode;{"language": "de"} | null | Then I expect node aggregate identifier "anthony-destinode" to lead to node cs-identifier;anthony-destinode;{"language": "de"} And I expect this node to be referenced by: | Name | Node | Properties | - | referenceProperty | cs-identifier;source-nodandaise;{"language": "de"} | null | + | reference | cs-identifier;source-nodandaise;{"language": "de"} | null | When I am in content stream "cs-identifier" and dimension space point {"language": "ch"} Then I expect node aggregate identifier "source-nodandaise" to lead to node cs-identifier;source-nodandaise;{"language": "de"} And I expect this node to have the following references: | Name | Node | Properties | - | referenceProperty | cs-identifier;anthony-destinode;{"language": "de"} | null | + | reference | cs-identifier;anthony-destinode;{"language": "de"} | null | Then I expect node aggregate identifier "anthony-destinode" to lead to node cs-identifier;anthony-destinode;{"language": "de"} And I expect this node to be referenced by: | Name | Node | Properties | - | referenceProperty | cs-identifier;source-nodandaise;{"language": "de"} | null | + | reference | cs-identifier;source-nodandaise;{"language": "de"} | null | Scenario: Create a reference, then create a generalization of the source node; and the references should exist on the generalization # We need to create a new ch-only node to test this; as by default, only a german node already exists shining through in ch @@ -256,7 +255,7 @@ Feature: Node References with Dimensions | Key | Value | | sourceNodeAggregateId | "ch-only" | | sourceOriginDimensionSpacePoint | {"language": "ch"} | - | referenceName | "referenceProperty" | + | referenceName | "reference" | | references | [{"target": "anthony-destinode"}] | And the graph projection is fully up to date @@ -273,20 +272,20 @@ Feature: Node References with Dimensions Then I expect node aggregate identifier "ch-only" to lead to node cs-identifier;ch-only;{"language": "de"} Then I expect this node to have the following references: | Name | Node | Properties | - | referenceProperty | cs-identifier;anthony-destinode;{"language": "de"} | null | + | reference | cs-identifier;anthony-destinode;{"language": "de"} | null | Then I expect node aggregate identifier "anthony-destinode" to lead to node cs-identifier;anthony-destinode;{"language": "de"} And I expect this node to be referenced by: | Name | Node | Properties | - | referenceProperty | cs-identifier;ch-only;{"language": "de"} | null | + | reference | cs-identifier;ch-only;{"language": "de"} | null | # the reference must also exist on the non-touched node When I am in content stream "cs-identifier" and dimension space point {"language": "ch"} Then I expect node aggregate identifier "ch-only" to lead to node cs-identifier;ch-only;{"language": "ch"} Then I expect this node to have the following references: | Name | Node | Properties | - | referenceProperty | cs-identifier;anthony-destinode;{"language": "de"} | null | + | reference | cs-identifier;anthony-destinode;{"language": "de"} | null | Then I expect node aggregate identifier "anthony-destinode" to lead to node cs-identifier;anthony-destinode;{"language": "de"} And I expect this node to be referenced by: | Name | Node | Properties | - | referenceProperty | cs-identifier;ch-only;{"language": "ch"} | null | + | reference | cs-identifier;ch-only;{"language": "ch"} | null | diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/06-NodeDisabling/02-DisableNodeAggregate_WithoutDimensions.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/06-NodeDisabling/02-DisableNodeAggregate_WithoutDimensions.feature index 9f8d73c1cf0..a55e0051b6e 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/06-NodeDisabling/02-DisableNodeAggregate_WithoutDimensions.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/06-NodeDisabling/02-DisableNodeAggregate_WithoutDimensions.feature @@ -10,9 +10,8 @@ Feature: Disable a node aggregate And using the following node types: """yaml 'Neos.ContentRepository.Testing:Document': - properties: - references: - type: references + references: + references: {} """ And using identifier "default", I define a content repository And I am in content repository "default" diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/06-NodeDisabling/03-DisableNodeAggregate_WithDimensions.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/06-NodeDisabling/03-DisableNodeAggregate_WithDimensions.feature index 9ab8ece1340..3547f672333 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/06-NodeDisabling/03-DisableNodeAggregate_WithDimensions.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/06-NodeDisabling/03-DisableNodeAggregate_WithDimensions.feature @@ -12,9 +12,8 @@ Feature: Disable a node aggregate And using the following node types: """yaml 'Neos.ContentRepository.Testing:Document': - properties: - references: - type: references + references: + references: {} """ And using identifier "default", I define a content repository And I am in content repository "default" diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/06-NodeDisabling/05-EnableNodeAggregate_WithoutDimensions.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/06-NodeDisabling/05-EnableNodeAggregate_WithoutDimensions.feature index 1a53bc1d35b..3ef4e8e5984 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/06-NodeDisabling/05-EnableNodeAggregate_WithoutDimensions.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/06-NodeDisabling/05-EnableNodeAggregate_WithoutDimensions.feature @@ -10,9 +10,8 @@ Feature: Enable a node aggregate And using the following node types: """yaml 'Neos.ContentRepository.Testing:Document': - properties: - references: - type: references + references: + references: {} """ And using identifier "default", I define a content repository And I am in content repository "default" diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/06-NodeDisabling/06-EnableNodeAggregate_WithDimensions.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/06-NodeDisabling/06-EnableNodeAggregate_WithDimensions.feature index eef0ffed0ed..3d130f013e0 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/06-NodeDisabling/06-EnableNodeAggregate_WithDimensions.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/06-NodeDisabling/06-EnableNodeAggregate_WithDimensions.feature @@ -12,9 +12,8 @@ Feature: Enable a node aggregate And using the following node types: """yaml 'Neos.ContentRepository.Testing:Document': - properties: - references: - type: references + references: + references: {} """ And using identifier "default", I define a content repository And I am in content repository "default" diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/07-NodeRemoval/02-RemoveNodeAggregate_WithoutDimensions.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/07-NodeRemoval/02-RemoveNodeAggregate_WithoutDimensions.feature index 093aa669a7a..6dbe73de5a1 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/07-NodeRemoval/02-RemoveNodeAggregate_WithoutDimensions.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/07-NodeRemoval/02-RemoveNodeAggregate_WithoutDimensions.feature @@ -10,9 +10,8 @@ Feature: Remove NodeAggregate And using the following node types: """yaml 'Neos.ContentRepository.Testing:Document': - properties: - references: - type: references + references: + references: {} """ And using identifier "default", I define a content repository And I am in content repository "default" diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/07-NodeRemoval/03-RemoveNodeAggregate_WithDimensions.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/07-NodeRemoval/03-RemoveNodeAggregate_WithDimensions.feature index 1186753cc69..edfc254c7d3 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/07-NodeRemoval/03-RemoveNodeAggregate_WithDimensions.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/07-NodeRemoval/03-RemoveNodeAggregate_WithDimensions.feature @@ -12,9 +12,8 @@ Feature: Remove NodeAggregate And using the following node types: """yaml 'Neos.ContentRepository.Testing:Document': - properties: - references: - type: references + references: + references: {} """ And using identifier "default", I define a content repository And I am in content repository "default" diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/ContentStreamForking/NodeReferencesOnForkContentStream.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/ContentStreamForking/NodeReferencesOnForkContentStream.feature index e16bb97b991..7fb2db3b3f9 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/ContentStreamForking/NodeReferencesOnForkContentStream.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/ContentStreamForking/NodeReferencesOnForkContentStream.feature @@ -12,12 +12,11 @@ Feature: On forking a content stream, node references should be copied as well. """yaml 'Neos.ContentRepository.Testing:NodeWithReferences': properties: - referenceProperty: - type: reference - referencesProperty: - type: references text: type: string + references: + reference: {} + references: {} """ And using identifier "default", I define a content repository And I am in content repository "default" @@ -44,7 +43,7 @@ Feature: On forking a content stream, node references should be copied as well. Given the command SetNodeReferences is executed with payload: | Key | Value | | sourceNodeAggregateId | "source-nodandaise" | - | referenceName | "referenceProperty" | + | referenceName | "reference" | | references | [{"target": "anthony-destinode"}] | And the graph projection is fully up to date @@ -59,21 +58,21 @@ Feature: On forking a content stream, node references should be copied as well. Then I expect node aggregate identifier "source-nodandaise" to lead to node user-cs-identifier;source-nodandaise;{"language": "de"} Then I expect this node to have the following references: | Name | Node | Properties | - | referenceProperty | user-cs-identifier;anthony-destinode;{"language": "de"} | null | + | reference | user-cs-identifier;anthony-destinode;{"language": "de"} | null | Then I expect node aggregate identifier "anthony-destinode" to lead to node user-cs-identifier;anthony-destinode;{"language": "de"} And I expect this node to be referenced by: | Name | Node | Properties | - | referenceProperty | user-cs-identifier;source-nodandaise;{"language": "de"} | null | + | reference | user-cs-identifier;source-nodandaise;{"language": "de"} | null | When I am in content stream "user-cs-identifier" and dimension space point {"language": "ch"} Then I expect node aggregate identifier "source-nodandaise" to lead to node user-cs-identifier;source-nodandaise;{"language": "de"} Then I expect this node to have the following references: | Name | Node | Properties | - | referenceProperty | user-cs-identifier;anthony-destinode;{"language": "de"} | null | + | reference | user-cs-identifier;anthony-destinode;{"language": "de"} | null | Then I expect node aggregate identifier "anthony-destinode" to lead to node user-cs-identifier;anthony-destinode;{"language": "de"} And I expect this node to be referenced by: | Name | Node | Properties | - | referenceProperty | user-cs-identifier;source-nodandaise;{"language": "de"} | null | + | reference | user-cs-identifier;source-nodandaise;{"language": "de"} | null | # after then modifying the node's properties (thus triggering copy-on-write), the reference property # should still exist (this was a BUG) @@ -87,18 +86,18 @@ Feature: On forking a content stream, node references should be copied as well. Then I expect node aggregate identifier "source-nodandaise" to lead to node user-cs-identifier;source-nodandaise;{"language": "de"} And I expect this node to have the following references: | Name | Node | Properties | - | referenceProperty | user-cs-identifier;anthony-destinode;{"language": "de"} | null | + | reference | user-cs-identifier;anthony-destinode;{"language": "de"} | null | Then I expect node aggregate identifier "anthony-destinode" to lead to node user-cs-identifier;anthony-destinode;{"language": "de"} And I expect this node to be referenced by: | Name | Node | Properties | - | referenceProperty | user-cs-identifier;source-nodandaise;{"language": "de"} | null | + | reference | user-cs-identifier;source-nodandaise;{"language": "de"} | null | When I am in content stream "user-cs-identifier" and dimension space point {"language": "ch"} Then I expect node aggregate identifier "source-nodandaise" to lead to node user-cs-identifier;source-nodandaise;{"language": "de"} And I expect this node to have the following references: | Name | Node | Properties | - | referenceProperty | user-cs-identifier;anthony-destinode;{"language": "de"} | null | + | reference | user-cs-identifier;anthony-destinode;{"language": "de"} | null | Then I expect node aggregate identifier "anthony-destinode" to lead to node user-cs-identifier;anthony-destinode;{"language": "de"} And I expect this node to be referenced by: | Name | Node | Properties | - | referenceProperty | user-cs-identifier;source-nodandaise;{"language": "de"} | null | + | reference | user-cs-identifier;source-nodandaise;{"language": "de"} | null | diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodePublishing/AllCommandsAreImplemented.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodePublishing/AllCommandsAreImplemented.feature index 6cab17a2920..1116c08314a 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodePublishing/AllCommandsAreImplemented.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodePublishing/AllCommandsAreImplemented.feature @@ -25,8 +25,8 @@ Feature: Publishing hide/show scenario of nodes properties: text: type: string - referenceProperty: - type: reference + references: + reference: {} 'Neos.ContentRepository.Testing:Image': properties: image: @@ -309,14 +309,14 @@ Feature: Publishing hide/show scenario of nodes | contentStreamId | "user-cs-identifier" | | sourceNodeAggregateId | "sir-david-nodenborough" | | sourceOriginDimensionSpacePoint | {} | - | referenceName | "referenceProperty" | + | referenceName | "reference" | | references | [{"target":"sir-nodeward-nodington-iii"}] | And the command SetNodeReferences is executed with payload: | Key | Value | | contentStreamId | "user-cs-identifier" | | sourceNodeAggregateId | "nody-mc-nodeface" | | sourceOriginDimensionSpacePoint | {} | - | referenceName | "referenceProperty" | + | referenceName | "reference" | | references | [{"target":"sir-nodeward-nodington-iii"}] | And the graph projection is fully up to date @@ -331,29 +331,29 @@ Feature: Publishing hide/show scenario of nodes Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{} And I expect this node to have the following references: | Name | Node | Properties | - | referenceProperty | cs-identifier;sir-nodeward-nodington-iii;{} | null | + | reference | cs-identifier;sir-nodeward-nodington-iii;{} | null | Then I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{} And I expect this node to have no references Then I expect node aggregate identifier "sir-nodeward-nodington-iii" to lead to node cs-identifier;sir-nodeward-nodington-iii;{} And I expect this node to have no references And I expect this node to be referenced by: | Name | Node | Properties | - | referenceProperty | cs-identifier;sir-david-nodenborough;{} | null | + | reference | cs-identifier;sir-david-nodenborough;{} | null | When I am in the active content stream of workspace "user-test" and dimension space point {} Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node user-cs-identifier-modified;sir-david-nodenborough;{} And I expect this node to have the following references: | Name | Node | Properties | - | referenceProperty | user-cs-identifier-modified;sir-nodeward-nodington-iii;{} | null | + | reference | user-cs-identifier-modified;sir-nodeward-nodington-iii;{} | null | Then I expect node aggregate identifier "nody-mc-nodeface" to lead to node user-cs-identifier-modified;nody-mc-nodeface;{} And I expect this node to have the following references: | Name | Node | Properties | - | referenceProperty | user-cs-identifier-modified;sir-nodeward-nodington-iii;{} | null | + | reference | user-cs-identifier-modified;sir-nodeward-nodington-iii;{} | null | Then I expect node aggregate identifier "sir-nodeward-nodington-iii" to lead to node user-cs-identifier-modified;sir-nodeward-nodington-iii;{} And I expect this node to be referenced by: | Name | Node | Properties | - | referenceProperty | user-cs-identifier-modified;nody-mc-nodeface;{} | null | - | referenceProperty | user-cs-identifier-modified;sir-david-nodenborough;{} | null | + | reference | user-cs-identifier-modified;nody-mc-nodeface;{} | null | + | reference | user-cs-identifier-modified;sir-david-nodenborough;{} | null | Scenario: (CreateNodeAggregateWithNode) It is possible to publish new nodes Given the command CreateWorkspace is executed with payload: diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeRemoval/RemoveNodeAggregateAfterDisabling.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeRemoval/RemoveNodeAggregateAfterDisabling.feature index 16d55fe52d5..976381d072b 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeRemoval/RemoveNodeAggregateAfterDisabling.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeRemoval/RemoveNodeAggregateAfterDisabling.feature @@ -10,9 +10,8 @@ Feature: Disable a node aggregate And using the following node types: """yaml 'Neos.ContentRepository.Testing:Document': - properties: - references: - type: references + references: + references: {} """ And using identifier "default", I define a content repository And I am in content repository "default" diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/FindChildNodeConnectedThroughEdgeName.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/FindChildNodeConnectedThroughEdgeName.feature index 67898ef2130..04e1b88be0c 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/FindChildNodeConnectedThroughEdgeName.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/FindChildNodeConnectedThroughEdgeName.feature @@ -12,13 +12,12 @@ Feature: Find nodes using the findChildNodeConnectedThroughEdgeName query properties: text: type: string + references: refs: - type: references properties: foo: type: string ref: - type: reference properties: foo: type: string diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/FindNodeById.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/FindNodeById.feature index 86070abf48c..1b6c20f9ccd 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/FindNodeById.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/FindNodeById.feature @@ -12,13 +12,12 @@ Feature: Find nodes using the findNodeById query properties: text: type: string + references: refs: - type: references properties: foo: type: string ref: - type: reference properties: foo: type: string diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/FindNodeByPath.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/FindNodeByPath.feature index 22ca151cffa..0d05f0a073d 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/FindNodeByPath.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/FindNodeByPath.feature @@ -15,13 +15,12 @@ Feature: Find nodes using the findNodeByPath query properties: text: type: string + references: refs: - type: references properties: foo: type: string ref: - type: reference properties: foo: type: string diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/FindParentNode.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/FindParentNode.feature index bcae519c37d..59749eebf38 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/FindParentNode.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/FindParentNode.feature @@ -12,13 +12,12 @@ Feature: Find nodes using the findParentNodes query properties: text: type: string + references: refs: - type: references properties: foo: type: string ref: - type: reference properties: foo: type: string diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/References.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/References.feature index a3a6bd5f958..0e0fbca5df4 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/References.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/References.feature @@ -22,13 +22,12 @@ Feature: Find and count references and their target nodes using the findReferenc type: integer dateProperty: type: DateTime + references: refs: - type: references properties: foo: type: string ref: - type: reference properties: foo: type: string diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/SiblingNodes.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/SiblingNodes.feature index 1f37538870b..4717cfa474b 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/SiblingNodes.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/SiblingNodes.feature @@ -12,13 +12,12 @@ Feature: Find sibling nodes using the findPrecedingSiblingNodes and findSucceedi properties: text: type: string + references: refs: - type: references properties: foo: type: string ref: - type: reference properties: foo: type: string diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/Timestamps.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/Timestamps.feature index ddb1b31533e..d9577684313 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/Timestamps.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/Timestamps.feature @@ -14,13 +14,12 @@ Feature: Behavior of Node timestamp properties "created", "originalCreated", "la properties: text: type: string + references: refs: - type: references properties: foo: type: string ref: - type: reference properties: foo: type: string diff --git a/Neos.ContentRepository.Core/Classes/Feature/Common/ConstraintChecks.php b/Neos.ContentRepository.Core/Classes/Feature/Common/ConstraintChecks.php index 7397fbf44f4..c5dd72b30e5 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/Common/ConstraintChecks.php +++ b/Neos.ContentRepository.Core/Classes/Feature/Common/ConstraintChecks.php @@ -194,16 +194,20 @@ protected function requireNodeTypeToDeclareProperty(NodeTypeName $nodeTypeName, } } - protected function requireNodeTypeToDeclareReference(NodeTypeName $nodeTypeName, ReferenceName $propertyName): void + protected function requireNodeTypeToDeclareReference(NodeTypeName $nodeTypeName, ReferenceName $referenceName): void { $nodeType = $this->getNodeTypeManager()->getNodeType($nodeTypeName->value); - if (isset($nodeType->getProperties()[$propertyName->value])) { - $propertyType = $nodeType->getPropertyType($propertyName->value); + if (isset($nodeType->getReferences()[$referenceName->value])) { + return; + } + /** @deprecated references should be declared as references */ + if (isset($nodeType->getProperties()[$referenceName->value])) { + $propertyType = $nodeType->getPropertyType($referenceName->value); if ($propertyType === 'reference' || $propertyType === 'references') { return; } } - throw ReferenceCannotBeSet::becauseTheNodeTypeDoesNotDeclareIt($propertyName, $nodeTypeName); + throw ReferenceCannotBeSet::becauseTheNodeTypeDoesNotDeclareIt($referenceName, $nodeTypeName); } protected function requireNodeTypeToAllowNodesOfTypeInReference( @@ -212,13 +216,15 @@ protected function requireNodeTypeToAllowNodesOfTypeInReference( NodeTypeName $nodeTypeNameInQuestion ): void { $nodeType = $this->getNodeTypeManager()->getNodeType($nodeTypeName->value); - $propertyDeclaration = $nodeType->getProperties()[$referenceName->value] ?? null; - if (is_null($propertyDeclaration)) { + $referenceDeclaration = $nodeType->getReferences()[$referenceName->value] + ?? $nodeType->getProperties()[$referenceName->value] /** @deprecated references sould be declared as such */ + ?? null; + if (is_null($referenceDeclaration)) { throw ReferenceCannotBeSet::becauseTheNodeTypeDoesNotDeclareIt($referenceName, $nodeTypeName); } - if (isset($propertyDeclaration['constraints']['nodeTypes'])) { + if (isset($referenceDeclaration['constraints']['nodeTypes'])) { $nodeTypeConstraints = NodeTypeConstraintsWithSubNodeTypes::createFromNodeTypeDeclaration( - $propertyDeclaration['constraints']['nodeTypes'], + $referenceDeclaration['constraints']['nodeTypes'], $this->getNodeTypeManager() ); if (!$nodeTypeConstraints->matches($nodeTypeNameInQuestion)) { @@ -637,7 +643,9 @@ protected function validateReferenceProperties( $nodeType = $this->nodeTypeManager->getNodeType($nodeTypeName->value); foreach ($referenceProperties->values as $propertyName => $propertyValue) { - $referencePropertyConfig = $nodeType->getProperties()[$referenceName->value]['properties'][$propertyName] + $referencePropertyConfig = $nodeType->getReferences()[$referenceName->value]['properties'][$propertyName] + /** @deprecated references should be declared as such */ + ?? $nodeType->getProperties()[$referenceName->value]['properties'][$propertyName] ?? null; if (is_null($referencePropertyConfig)) { diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeReferencing/NodeReferencing.php b/Neos.ContentRepository.Core/Classes/Feature/NodeReferencing/NodeReferencing.php index e74797f6989..c9e5901c1f5 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/NodeReferencing/NodeReferencing.php +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeReferencing/NodeReferencing.php @@ -136,7 +136,8 @@ private function handleSetSerializedNodeReferences( } $sourceNodeType = $this->requireNodeType($sourceNodeAggregate->nodeTypeName); - $scopeDeclaration = $sourceNodeType->getProperties()[$command->referenceName->value]['scope'] ?? ''; + $scopeDeclaration = $sourceNodeType->getReferences()[$command->referenceName->value]['scope'] + ?? $sourceNodeType->getProperties()[$command->referenceName->value]['scope'] ?? ''; $scope = PropertyScope::tryFrom($scopeDeclaration) ?: PropertyScope::SCOPE_NODE; $affectedOrigins = $scope->resolveAffectedOrigins( diff --git a/Neos.ContentRepository.Core/Classes/Infrastructure/Property/PropertyConverter.php b/Neos.ContentRepository.Core/Classes/Infrastructure/Property/PropertyConverter.php index 12fb9e14402..89a0203e9db 100644 --- a/Neos.ContentRepository.Core/Classes/Infrastructure/Property/PropertyConverter.php +++ b/Neos.ContentRepository.Core/Classes/Infrastructure/Property/PropertyConverter.php @@ -104,7 +104,9 @@ public function serializeReferencePropertyValues( foreach ($propertyValuesToWrite->values as $propertyName => $propertyValue) { // reference properties are always completely overwritten, // so we don't need the node properties' unset option - $declaredType = $nodeType->getProperties()[$referenceName->value]['properties'][$propertyName]['type']; + $declaredType = $nodeType->getReferences()[$referenceName->value]['properties'][$propertyName]['type'] + /** @deprecated references should be declared as such */ + ?? $nodeType->getProperties()[$referenceName->value]['properties'][$propertyName]['type']; $serializedPropertyValues[$propertyName] = $this->serializePropertyValue( $declaredType, diff --git a/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php b/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php index 1eaae89553c..fc70d3484cc 100644 --- a/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php +++ b/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php @@ -399,6 +399,21 @@ public function getProperties(): array return ($this->fullConfiguration['properties'] ?? []); } + /** + * Return the array with the defined references. The key is the reference name, + * the value the reference configuration. There are no guarantees on how the + * reference configuration looks like. + * + * @return array + * @api + */ + public function getReferences(): array + { + $this->initialize(); + + return ($this->fullConfiguration['references'] ?? []); + } + /** * Returns the configured type of the specified property * diff --git a/Neos.ContentRepository.TestSuite/Classes/Behavior/Features/Bootstrap/GenericCommandExecutionAndEventPublication.php b/Neos.ContentRepository.TestSuite/Classes/Behavior/Features/Bootstrap/GenericCommandExecutionAndEventPublication.php index 8f9680a2736..9daa2712071 100644 --- a/Neos.ContentRepository.TestSuite/Classes/Behavior/Features/Bootstrap/GenericCommandExecutionAndEventPublication.php +++ b/Neos.ContentRepository.TestSuite/Classes/Behavior/Features/Bootstrap/GenericCommandExecutionAndEventPublication.php @@ -160,9 +160,10 @@ public function theLastCommandShouldHaveThrown(string $shortExceptionName, ?int Assert::assertSame($shortExceptionName, $lastCommandExceptionShortName, sprintf('Actual exception: %s (%s): %s', get_class($this->lastCommandException), $this->lastCommandException->getCode(), $this->lastCommandException->getMessage())); if (!is_null($expectedCode)) { Assert::assertSame($expectedCode, $this->lastCommandException->getCode(), sprintf( - 'Expected exception code %s, got exception code %s instead', + 'Expected exception code %s, got exception code %s instead; Message: %s', $expectedCode, - $this->lastCommandException->getCode() + $this->lastCommandException->getCode(), + $this->lastCommandException->getMessage() )); } } From 8139f161f91014361800245a1147e27fb1e87764 Mon Sep 17 00:00:00 2001 From: Bernhard Schmitt Date: Thu, 2 Nov 2023 11:32:41 +0100 Subject: [PATCH 02/23] 4549 - WIP: separate properties and references on setFullConfiguration --- .../Classes/Feature/Common/ConstraintChecks.php | 13 +------------ .../Feature/NodeReferencing/NodeReferencing.php | 3 +-- .../Property/PropertyConverter.php | 4 +--- .../Classes/NodeType/NodeType.php | 17 +++++++++++++++++ 4 files changed, 20 insertions(+), 17 deletions(-) diff --git a/Neos.ContentRepository.Core/Classes/Feature/Common/ConstraintChecks.php b/Neos.ContentRepository.Core/Classes/Feature/Common/ConstraintChecks.php index c5dd72b30e5..1a343c3ec5c 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/Common/ConstraintChecks.php +++ b/Neos.ContentRepository.Core/Classes/Feature/Common/ConstraintChecks.php @@ -200,13 +200,6 @@ protected function requireNodeTypeToDeclareReference(NodeTypeName $nodeTypeName, if (isset($nodeType->getReferences()[$referenceName->value])) { return; } - /** @deprecated references should be declared as references */ - if (isset($nodeType->getProperties()[$referenceName->value])) { - $propertyType = $nodeType->getPropertyType($referenceName->value); - if ($propertyType === 'reference' || $propertyType === 'references') { - return; - } - } throw ReferenceCannotBeSet::becauseTheNodeTypeDoesNotDeclareIt($referenceName, $nodeTypeName); } @@ -216,9 +209,7 @@ protected function requireNodeTypeToAllowNodesOfTypeInReference( NodeTypeName $nodeTypeNameInQuestion ): void { $nodeType = $this->getNodeTypeManager()->getNodeType($nodeTypeName->value); - $referenceDeclaration = $nodeType->getReferences()[$referenceName->value] - ?? $nodeType->getProperties()[$referenceName->value] /** @deprecated references sould be declared as such */ - ?? null; + $referenceDeclaration = $nodeType->getReferences()[$referenceName->value] ?? null; if (is_null($referenceDeclaration)) { throw ReferenceCannotBeSet::becauseTheNodeTypeDoesNotDeclareIt($referenceName, $nodeTypeName); } @@ -644,8 +635,6 @@ protected function validateReferenceProperties( foreach ($referenceProperties->values as $propertyName => $propertyValue) { $referencePropertyConfig = $nodeType->getReferences()[$referenceName->value]['properties'][$propertyName] - /** @deprecated references should be declared as such */ - ?? $nodeType->getProperties()[$referenceName->value]['properties'][$propertyName] ?? null; if (is_null($referencePropertyConfig)) { diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeReferencing/NodeReferencing.php b/Neos.ContentRepository.Core/Classes/Feature/NodeReferencing/NodeReferencing.php index c9e5901c1f5..2b3268398fa 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/NodeReferencing/NodeReferencing.php +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeReferencing/NodeReferencing.php @@ -136,8 +136,7 @@ private function handleSetSerializedNodeReferences( } $sourceNodeType = $this->requireNodeType($sourceNodeAggregate->nodeTypeName); - $scopeDeclaration = $sourceNodeType->getReferences()[$command->referenceName->value]['scope'] - ?? $sourceNodeType->getProperties()[$command->referenceName->value]['scope'] ?? ''; + $scopeDeclaration = $sourceNodeType->getReferences()[$command->referenceName->value]['scope'] ?? ''; $scope = PropertyScope::tryFrom($scopeDeclaration) ?: PropertyScope::SCOPE_NODE; $affectedOrigins = $scope->resolveAffectedOrigins( diff --git a/Neos.ContentRepository.Core/Classes/Infrastructure/Property/PropertyConverter.php b/Neos.ContentRepository.Core/Classes/Infrastructure/Property/PropertyConverter.php index 89a0203e9db..f546702531f 100644 --- a/Neos.ContentRepository.Core/Classes/Infrastructure/Property/PropertyConverter.php +++ b/Neos.ContentRepository.Core/Classes/Infrastructure/Property/PropertyConverter.php @@ -104,9 +104,7 @@ public function serializeReferencePropertyValues( foreach ($propertyValuesToWrite->values as $propertyName => $propertyValue) { // reference properties are always completely overwritten, // so we don't need the node properties' unset option - $declaredType = $nodeType->getReferences()[$referenceName->value]['properties'][$propertyName]['type'] - /** @deprecated references should be declared as such */ - ?? $nodeType->getProperties()[$referenceName->value]['properties'][$propertyName]['type']; + $declaredType = $nodeType->getReferences()[$referenceName->value]['properties'][$propertyName]['type'] ?? 'string'; $serializedPropertyValues[$propertyName] = $this->serializePropertyValue( $declaredType, diff --git a/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php b/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php index fc70d3484cc..031833d070e 100644 --- a/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php +++ b/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php @@ -502,6 +502,23 @@ public function allowsChildNodeType(NodeType $nodeType): bool */ protected function setFullConfiguration(array $fullConfiguration): void { + $referencesConfiguration = $fullConfiguration['references'] ?? []; + foreach ($fullConfiguration['properties'] ?? [] as $propertyName => $propertyConfiguration) { + $propertyType = $propertyConfiguration['type'] ?? 'string'; + switch ($propertyType) { + case 'reference': + unset($propertyConfiguration['type']); + $propertyConfiguration['constraints']['valueReference']['maxValue'] = 1; + $referencesConfiguration[$propertyName] = $propertyConfiguration; + unset($fullConfiguration['properties'][$propertyName]); + case 'references': + unset($propertyConfiguration['type']); + $referencesConfiguration[$propertyName] = $propertyConfiguration; + unset($fullConfiguration['properties'][$propertyName]); + default: + } + } + $fullConfiguration['references'] = $referencesConfiguration; $this->fullConfiguration = $fullConfiguration; } } From 8412c4fe42458688c529985dd0132bfdd0099d7c Mon Sep 17 00:00:00 2001 From: Bernhard Schmitt Date: Thu, 2 Nov 2023 16:09:33 +0100 Subject: [PATCH 03/23] 4549 - Merge references to properties for now --- Neos.Neos/Classes/Service/NodeTypeSchemaBuilder.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Neos.Neos/Classes/Service/NodeTypeSchemaBuilder.php b/Neos.Neos/Classes/Service/NodeTypeSchemaBuilder.php index b48f920c261..5e251455661 100644 --- a/Neos.Neos/Classes/Service/NodeTypeSchemaBuilder.php +++ b/Neos.Neos/Classes/Service/NodeTypeSchemaBuilder.php @@ -18,6 +18,7 @@ use Neos\ContentRepository\Core\NodeType\NodeTypeManager; use Neos\ContentRepository\Core\NodeType\NodeType; use Neos\ContentRepository\Core\SharedModel\Node\NodeName; +use Neos\Utility\Arrays; /** * Renders the Node Type Schema in a format the User Interface understands; @@ -67,6 +68,11 @@ public function generateNodeTypeSchema() foreach ($nodeTypes as $nodeTypeName => $nodeType) { if ($nodeType->isAbstract() === false) { $configuration = $nodeType->getFullConfiguration(); + $configuration['properties'] = Arrays::arrayMergeRecursiveOverrule( + $configuration['properties'] ?? [], + $configuration['references'] ?? [], + ); + unset($configuration['references']); $schema['nodeTypes'][$nodeTypeName] = $configuration; $schema['nodeTypes'][$nodeTypeName]['label'] = $nodeType->getLabel(); } From 7c2574c08894225c1ebbd6ad6698d421173ad046 Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Thu, 16 Nov 2023 16:23:09 +0100 Subject: [PATCH 04/23] TASK: Make use of `requireNodeTypeToDeclareProperty` --- .../Classes/Feature/Common/ConstraintChecks.php | 7 ++++++- .../Classes/Feature/NodeCreation/NodeCreation.php | 7 +------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Neos.ContentRepository.Core/Classes/Feature/Common/ConstraintChecks.php b/Neos.ContentRepository.Core/Classes/Feature/Common/ConstraintChecks.php index 82fa9a015c8..ac3257b30df 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/Common/ConstraintChecks.php +++ b/Neos.ContentRepository.Core/Classes/Feature/Common/ConstraintChecks.php @@ -19,6 +19,7 @@ use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePointSet; use Neos\ContentRepository\Core\DimensionSpace\Exception\DimensionSpacePointNotFound; use Neos\ContentRepository\Core\NodeType\ConstraintCheck; +use Neos\ContentRepository\Core\SharedModel\Exception\PropertyCannotBeSet; use Neos\ContentRepository\Core\SharedModel\Exception\RootNodeAggregateDoesNotExist; use Neos\ContentRepository\Core\SharedModel\Exception\ContentStreamDoesNotExistYet; use Neos\ContentRepository\Core\SharedModel\Exception\DimensionSpacePointIsNotYetOccupied; @@ -191,7 +192,11 @@ protected function requireTetheredDescendantNodeTypesToNotBeOfTypeRoot(NodeType protected function requireNodeTypeToDeclareProperty(NodeTypeName $nodeTypeName, PropertyName $propertyName): void { $nodeType = $this->getNodeTypeManager()->getNodeType($nodeTypeName->value); - if (!isset($nodeType->getProperties()[$propertyName->value])) { + if (!$nodeType->hasProperty($propertyName->value)) { + throw PropertyCannotBeSet::becauseTheNodeTypeDoesNotDeclareIt( + $propertyName, + $nodeTypeName + ); } } diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeCreation/NodeCreation.php b/Neos.ContentRepository.Core/Classes/Feature/NodeCreation/NodeCreation.php index 07c0518e54b..3ee2cb65850 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/NodeCreation/NodeCreation.php +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeCreation/NodeCreation.php @@ -122,12 +122,7 @@ private function validateProperties(?PropertyValuesToWrite $propertyValues, Node $nodeType = $this->nodeTypeManager->getNodeType($nodeTypeName->value); foreach ($propertyValues->values as $propertyName => $propertyValue) { - if (!isset($nodeType->getProperties()[$propertyName])) { - throw PropertyCannotBeSet::becauseTheNodeTypeDoesNotDeclareIt( - PropertyName::fromString($propertyName), - $nodeTypeName - ); - } + $this->requireNodeTypeToDeclareProperty($nodeTypeName, PropertyName::fromString($propertyName)); $propertyType = PropertyType::fromNodeTypeDeclaration( $nodeType->getPropertyType($propertyName), PropertyName::fromString($propertyName), From ebd5c895c03d648540abde5f498d82751c3172be Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Thu, 16 Nov 2023 17:01:22 +0100 Subject: [PATCH 05/23] FEATURE: Add `constraints.maxItems` for references --- ...SetNodeReferences_ConstraintChecks.feature | 12 +++++++++- .../Feature/Common/ConstraintChecks.php | 23 +++++++++++++++++-- .../Dto/NodeReferencesToWrite.php | 7 +++++- .../NodeReferencing/NodeReferencing.php | 6 +++++ .../Classes/NodeType/NodeType.php | 5 ++-- .../Exception/ReferenceCannotBeSet.php | 16 +++++++++++-- 6 files changed, 61 insertions(+), 8 deletions(-) diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/05-NodeReferencing/01-SetNodeReferences_ConstraintChecks.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/05-NodeReferencing/01-SetNodeReferences_ConstraintChecks.feature index dded08fe2ec..d88b78a9989 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/05-NodeReferencing/01-SetNodeReferences_ConstraintChecks.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/05-NodeReferencing/01-SetNodeReferences_ConstraintChecks.feature @@ -14,7 +14,9 @@ Feature: Constraint checks on SetNodeReferences nonReferenceProperty: type: string references: - reference: {} + reference: + constraints: + maxItems: 1 references: {} constrainedReference: constraints: @@ -112,6 +114,14 @@ Feature: Constraint checks on SetNodeReferences | references | [{"target":"lady-eleonode-rootford"}] | Then the last command should have thrown an exception of type "NodeAggregateIsRoot" + Scenario: Try to set references exceeding the maxItems count + When the command SetNodeReferences is executed with payload and exceptions are caught: + | Key | Value | + | sourceNodeAggregateId | "source-nodandaise" | + | referenceName | "reference" | + | references | [{"target":"anthony-destinode"}, {"target":"berta-destinode"}] | + Then the last command should have thrown an exception of type "ReferenceCannotBeSet" with code 1700150156 + Scenario: Try to reference a node aggregate of a type not matching the constraints When the command SetNodeReferences is executed with payload and exceptions are caught: | Key | Value | diff --git a/Neos.ContentRepository.Core/Classes/Feature/Common/ConstraintChecks.php b/Neos.ContentRepository.Core/Classes/Feature/Common/ConstraintChecks.php index ac3257b30df..9033081cd9a 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/Common/ConstraintChecks.php +++ b/Neos.ContentRepository.Core/Classes/Feature/Common/ConstraintChecks.php @@ -18,6 +18,7 @@ use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePoint; use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePointSet; use Neos\ContentRepository\Core\DimensionSpace\Exception\DimensionSpacePointNotFound; +use Neos\ContentRepository\Core\Feature\NodeReferencing\Dto\SerializedNodeReferences; use Neos\ContentRepository\Core\NodeType\ConstraintCheck; use Neos\ContentRepository\Core\SharedModel\Exception\PropertyCannotBeSet; use Neos\ContentRepository\Core\SharedModel\Exception\RootNodeAggregateDoesNotExist; @@ -223,7 +224,7 @@ protected function requireNodeTypeToAllowNodesOfTypeInReference( $constraints = $referenceDeclaration['constraints']['nodeTypes'] ?? []; if (!ConstraintCheck::create($constraints)->isNodeTypeAllowed($nodeType)) { - throw ReferenceCannotBeSet::becauseTheConstraintsAreNotMatched( + throw ReferenceCannotBeSet::becauseTheNodeTypeConstraintsAreNotMatched( $referenceName, $nodeTypeName, $nodeTypeNameInQuestion @@ -231,6 +232,24 @@ protected function requireNodeTypeToAllowNodesOfTypeInReference( } } + protected function requireNodeTypeToAllowCountOfReferencesInReference(SerializedNodeReferences $nodeReferences, ReferenceName $referenceName, NodeTypeName $nodeTypeName): void + { + $nodeType = $this->nodeTypeManager->getNodeType($nodeTypeName->value); + + $maxItems = $nodeType->getReferences()[$referenceName->value]['constraints']['maxItems'] ?? null; + if ($maxItems === null) { + return; + } + + if ($maxItems < count($nodeReferences)) { + throw ReferenceCannotBeSet::becauseTheItemsCountConstraintsAreNotMatched( + $referenceName, + $nodeTypeName, + count($nodeReferences) + ); + } + } + /** * NodeType and NodeName must belong together to the same node, which is the to-be-checked one. * @@ -641,7 +660,7 @@ protected function validateReferenceProperties( ?? null; if (is_null($referencePropertyConfig)) { - throw ReferenceCannotBeSet::becauseTheItDoesNotDeclareAProperty( + throw ReferenceCannotBeSet::becauseTheReferenceDoesNotDeclareTheProperty( $referenceName, $nodeTypeName, PropertyName::fromString($propertyName) diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeReferencing/Dto/NodeReferencesToWrite.php b/Neos.ContentRepository.Core/Classes/Feature/NodeReferencing/Dto/NodeReferencesToWrite.php index fd6f36af88b..8ba90b34b43 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/NodeReferencing/Dto/NodeReferencesToWrite.php +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeReferencing/Dto/NodeReferencesToWrite.php @@ -26,7 +26,7 @@ * @implements \IteratorAggregate * @api used as part of commands */ -final readonly class NodeReferencesToWrite implements \IteratorAggregate, \JsonSerializable +final readonly class NodeReferencesToWrite implements \IteratorAggregate, \Countable, \JsonSerializable { /** * @var array @@ -90,4 +90,9 @@ public function jsonSerialize(): array { return $this->references; } + + public function count(): int + { + return count($this->references); + } } diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeReferencing/NodeReferencing.php b/Neos.ContentRepository.Core/Classes/Feature/NodeReferencing/NodeReferencing.php index 2b3268398fa..3ca9f56b76b 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/NodeReferencing/NodeReferencing.php +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeReferencing/NodeReferencing.php @@ -116,6 +116,12 @@ private function handleSetSerializedNodeReferences( ); $this->requireNodeTypeToDeclareReference($sourceNodeAggregate->nodeTypeName, $command->referenceName); + $this->requireNodeTypeToAllowCountOfReferencesInReference( + $command->references, + $command->referenceName, + $sourceNodeAggregate->nodeTypeName + ); + foreach ($command->references as $reference) { assert($reference instanceof SerializedNodeReference); $destinationNodeAggregate = $this->requireProjectedNodeAggregate( diff --git a/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php b/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php index 46a0b267a1f..ce152f0d25a 100644 --- a/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php +++ b/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php @@ -526,14 +526,15 @@ protected function setFullConfiguration(array $fullConfiguration): void switch ($propertyType) { case 'reference': unset($propertyConfiguration['type']); - $propertyConfiguration['constraints']['valueReference']['maxValue'] = 1; + $propertyConfiguration['constraints']['maxItems'] = 1; $referencesConfiguration[$propertyName] = $propertyConfiguration; unset($fullConfiguration['properties'][$propertyName]); + break; case 'references': unset($propertyConfiguration['type']); $referencesConfiguration[$propertyName] = $propertyConfiguration; unset($fullConfiguration['properties'][$propertyName]); - default: + break; } } $fullConfiguration['references'] = $referencesConfiguration; diff --git a/Neos.ContentRepository.Core/Classes/SharedModel/Exception/ReferenceCannotBeSet.php b/Neos.ContentRepository.Core/Classes/SharedModel/Exception/ReferenceCannotBeSet.php index 47929baa74e..cf3e6a638a4 100644 --- a/Neos.ContentRepository.Core/Classes/SharedModel/Exception/ReferenceCannotBeSet.php +++ b/Neos.ContentRepository.Core/Classes/SharedModel/Exception/ReferenceCannotBeSet.php @@ -36,7 +36,7 @@ public static function becauseTheNodeTypeDoesNotDeclareIt( ); } - public static function becauseTheConstraintsAreNotMatched( + public static function becauseTheNodeTypeConstraintsAreNotMatched( ReferenceName $referenceName, NodeTypeName $nodeTypeName, NodeTypeName $nameOfAttemptedType @@ -48,7 +48,19 @@ public static function becauseTheConstraintsAreNotMatched( ); } - public static function becauseTheItDoesNotDeclareAProperty( + public static function becauseTheItemsCountConstraintsAreNotMatched( + ReferenceName $referenceName, + NodeTypeName $nodeTypeName, + int $countOfAttemptedReferencesToWrite + ): self { + return new self( + 'Reference "' . $referenceName->value . '" cannot be set for node type "' + . $nodeTypeName->value . '" because the constraints do not allow to set ' . $countOfAttemptedReferencesToWrite . ' references', + 1700150156 + ); + } + + public static function becauseTheReferenceDoesNotDeclareTheProperty( ReferenceName $referenceName, NodeTypeName $nodeTypeName, PropertyName $propertyName From faab0796ce2a7c10242a1c0a519431e7c94edf28 Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Thu, 16 Nov 2023 17:17:32 +0100 Subject: [PATCH 06/23] BUGFIX: Prevent to set twice the same target in a reference --- .../01-SetNodeReferences_ConstraintChecks.feature | 8 ++++++++ .../NodeReferencing/Dto/SerializedNodeReferences.php | 7 +++++++ 2 files changed, 15 insertions(+) diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/05-NodeReferencing/01-SetNodeReferences_ConstraintChecks.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/05-NodeReferencing/01-SetNodeReferences_ConstraintChecks.feature index d88b78a9989..89f7bd32936 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/05-NodeReferencing/01-SetNodeReferences_ConstraintChecks.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/05-NodeReferencing/01-SetNodeReferences_ConstraintChecks.feature @@ -181,3 +181,11 @@ Feature: Constraint checks on SetNodeReferences | referenceName | "referenceWithProperties" | | references | [{"target":"anthony-destinode", "properties":{"postalAddress": "28 31st of February Street"}}] | Then the last command should have thrown an exception of type "ReferenceCannotBeSet" with code 1658406762 + + Scenario: Node reference cannot hold multiple targets to the same node + When the command SetNodeReferences is executed with payload and exceptions are caught: + | Key | Value | + | sourceNodeAggregateId | "source-nodandaise" | + | referenceName | "references" | + | references | [{"target":"anthony-destinode"}, {"target":"anthony-destinode"}] | + Then the last command should have thrown an exception of type "InvalidArgumentException" with code 1700150910 diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeReferencing/Dto/SerializedNodeReferences.php b/Neos.ContentRepository.Core/Classes/Feature/NodeReferencing/Dto/SerializedNodeReferences.php index 85dc99aa39c..809f1c196e5 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/NodeReferencing/Dto/SerializedNodeReferences.php +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeReferencing/Dto/SerializedNodeReferences.php @@ -32,6 +32,13 @@ private function __construct(SerializedNodeReference ...$references) { + $existingTargets = []; + foreach ($references as $reference) { + if (isset($existingTargets[$reference->targetNodeAggregateId->value])) { + throw new \InvalidArgumentException(sprintf('Duplicate entry in references to write. Target "%s" already exists in collection.', $reference->targetNodeAggregateId->value), 1700150910); + } + $existingTargets[$reference->targetNodeAggregateId->value] = true; + } $this->references = $references; } From 1ef997aa57926dc43271d9bbdd0bcedbd639be34 Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Thu, 16 Nov 2023 21:01:25 +0100 Subject: [PATCH 07/23] WIP: Preparations for the Neos Ui to work again - added `hasReference` - __legacyPropertyType used to ensure that the FlowQuery property operation will return the node directly but not an array of nodes - remove type "reference" or "references" in php doc documentation - use `hasReference` instead of `getPropertyType === reference` --- .../Classes/Feature/Common/ConstraintChecks.php | 9 ++------- .../Dto/SerializedPropertyValues.php | 17 +++++++---------- .../Infrastructure/Property/PropertyType.php | 6 ------ .../Classes/NodeType/NodeType.php | 13 +++++++++++++ .../ContentGraph/ContentSubgraphInterface.php | 1 - .../Projection/ContentGraph/References.php | 4 +--- .../Exception/PropertyTypeIsInvalid.php | 9 --------- .../FlowQueryOperations/PropertyOperation.php | 13 +++++++------ .../Controller/Service/NodesController.php | 1 + .../Service/Mapping/NodeReferenceConverter.php | 1 + .../Classes/Service/NodeTypeSchemaBuilder.php | 4 +--- 11 files changed, 33 insertions(+), 45 deletions(-) diff --git a/Neos.ContentRepository.Core/Classes/Feature/Common/ConstraintChecks.php b/Neos.ContentRepository.Core/Classes/Feature/Common/ConstraintChecks.php index 9033081cd9a..7bb17f3e207 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/Common/ConstraintChecks.php +++ b/Neos.ContentRepository.Core/Classes/Feature/Common/ConstraintChecks.php @@ -204,7 +204,7 @@ protected function requireNodeTypeToDeclareProperty(NodeTypeName $nodeTypeName, protected function requireNodeTypeToDeclareReference(NodeTypeName $nodeTypeName, ReferenceName $referenceName): void { $nodeType = $this->getNodeTypeManager()->getNodeType($nodeTypeName->value); - if (isset($nodeType->getReferences()[$referenceName->value])) { + if ($nodeType->hasReference($referenceName->value)) { return; } throw ReferenceCannotBeSet::becauseTheNodeTypeDoesNotDeclareIt($referenceName, $nodeTypeName); @@ -216,12 +216,7 @@ protected function requireNodeTypeToAllowNodesOfTypeInReference( NodeTypeName $nodeTypeNameInQuestion ): void { $nodeType = $this->getNodeTypeManager()->getNodeType($nodeTypeName->value); - $referenceDeclaration = $nodeType->getReferences()[$referenceName->value] ?? null; - if (is_null($referenceDeclaration)) { - throw ReferenceCannotBeSet::becauseTheNodeTypeDoesNotDeclareIt($referenceName, $nodeTypeName); - } - - $constraints = $referenceDeclaration['constraints']['nodeTypes'] ?? []; + $constraints = $nodeType->getReferences()[$referenceName->value]['constraints']['nodeTypes'] ?? []; if (!ConstraintCheck::create($constraints)->isNodeTypeAllowed($nodeType)) { throw ReferenceCannotBeSet::becauseTheNodeTypeConstraintsAreNotMatched( diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeModification/Dto/SerializedPropertyValues.php b/Neos.ContentRepository.Core/Classes/Feature/NodeModification/Dto/SerializedPropertyValues.php index 9b1f162e6fe..6db96e7bae8 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/NodeModification/Dto/SerializedPropertyValues.php +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeModification/Dto/SerializedPropertyValues.php @@ -75,8 +75,14 @@ public static function defaultFromNodeType(NodeType $nodeType): self $defaultValue = json_encode($defaultValue); } + if ($nodeType->hasReference($propertyName)) { + throw new \InvalidArgumentException( + 'References cannot be serialized; you need to use the SetNodeReferences command instead.', + 1700154728 + ); + } + $propertyTypeFromSchema = $nodeType->getPropertyType($propertyName); - self::assertTypeIsNoReference($propertyTypeFromSchema); $values[$propertyName] = new SerializedPropertyValue($defaultValue, $propertyTypeFromSchema); } @@ -89,15 +95,6 @@ public static function fromJsonString(string $jsonString): self return self::fromArray(\json_decode($jsonString, true)); } - private static function assertTypeIsNoReference(string $propertyTypeFromSchema): void - { - if ($propertyTypeFromSchema === 'reference' || $propertyTypeFromSchema === 'references') { - throw new \RuntimeException( - 'TODO: references cannot be serialized; you need to use the SetNodeReferences command instead.' - ); - } - } - public function merge(self $other): self { // here, we skip null values diff --git a/Neos.ContentRepository.Core/Classes/Infrastructure/Property/PropertyType.php b/Neos.ContentRepository.Core/Classes/Infrastructure/Property/PropertyType.php index dcf83c5b7e4..f3013c934d7 100644 --- a/Neos.ContentRepository.Core/Classes/Infrastructure/Property/PropertyType.php +++ b/Neos.ContentRepository.Core/Classes/Infrastructure/Property/PropertyType.php @@ -64,9 +64,6 @@ public static function fromNodeTypeDeclaration( PropertyName $propertyName, NodeTypeName $nodeTypeName ): self { - if ($declaration === 'reference' || $declaration === 'references') { - throw PropertyTypeIsInvalid::becauseItIsReference($propertyName, $nodeTypeName); - } $type = self::tryFromString($declaration); if (!$type) { throw PropertyTypeIsInvalid::becauseItIsUndefined($propertyName, $declaration, $nodeTypeName); @@ -76,9 +73,6 @@ public static function fromNodeTypeDeclaration( private static function tryFromString(string $declaration): ?self { - if ($declaration === 'reference' || $declaration === 'references') { - return null; - } if ($declaration === 'bool' || $declaration === 'boolean') { return self::bool(); } diff --git a/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php b/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php index ce152f0d25a..b1d08b0e875 100644 --- a/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php +++ b/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php @@ -399,6 +399,16 @@ public function getProperties(): array return ($this->fullConfiguration['properties'] ?? []); } + /** + * Check if the property is configured in the schema. + */ + public function hasReference(string $referenceName): bool + { + $this->initialize(); + + return isset($this->fullConfiguration['references'][$referenceName]); + } + /** * Return the array with the defined references. The key is the reference name, * the value the reference configuration. There are no guarantees on how the @@ -527,6 +537,9 @@ protected function setFullConfiguration(array $fullConfiguration): void case 'reference': unset($propertyConfiguration['type']); $propertyConfiguration['constraints']['maxItems'] = 1; + // @deprecated remove with 10 + // used to ensure that the FlowQuery property operation will return the node directly but not an array of nodes + $propertyConfiguration['__legacyPropertyType'] = 'reference'; $referencesConfiguration[$propertyName] = $propertyConfiguration; unset($fullConfiguration['properties'][$propertyName]); break; diff --git a/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/ContentSubgraphInterface.php b/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/ContentSubgraphInterface.php index 5f65fb6a0f1..ed740805fb9 100644 --- a/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/ContentSubgraphInterface.php +++ b/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/ContentSubgraphInterface.php @@ -151,7 +151,6 @@ public function findSubtree(NodeAggregateId $entryNodeAggregateId, Filter\FindSu /** * Find all "outgoing" references of a given node that match the specified $filter * - * A reference is a node property of type "reference" or "references" * Because each reference has a name and can contain properties itself, this method does not return the target nodes * directly, but a collection of references {@see References}. * The corresponding nodes can be retrieved via {@see References::getNodes()} diff --git a/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/References.php b/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/References.php index 2917e5eeda1..bdd2381409e 100644 --- a/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/References.php +++ b/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/References.php @@ -17,8 +17,6 @@ /** * An immutable, 0-indexed, type-safe collection of Reference objects * - * A reference is a node property of type "reference" or "references" - * * Each reference describes the edge with its properties to another node. * * - references: @@ -31,7 +29,7 @@ * * The properties {@see Reference::$properties} are declared directly on the reference, and can provide information how one node is linked to another. * - * If multiple "outgoing" references are allowed via type "references", this collection will return multiple references with the same name {@see Reference::$name}. + * This collection might return multiple references with the same name {@see Reference::$name} if multiple "outgoing" references were set. * * @implements \IteratorAggregate * @implements \ArrayAccess diff --git a/Neos.ContentRepository.Core/Classes/SharedModel/Exception/PropertyTypeIsInvalid.php b/Neos.ContentRepository.Core/Classes/SharedModel/Exception/PropertyTypeIsInvalid.php index 8b5c241e063..c62b6e64dbd 100644 --- a/Neos.ContentRepository.Core/Classes/SharedModel/Exception/PropertyTypeIsInvalid.php +++ b/Neos.ContentRepository.Core/Classes/SharedModel/Exception/PropertyTypeIsInvalid.php @@ -24,15 +24,6 @@ */ final class PropertyTypeIsInvalid extends \DomainException { - public static function becauseItIsReference(PropertyName $propertyName, NodeTypeName $nodeTypeName): self - { - return new self( - 'Given property "' . $propertyName->value . '" is declared as "reference" in node type "' - . $nodeTypeName->value . '" and must be treated as such.', - 1630063201 - ); - } - public static function becauseItIsUndefined( PropertyName $propertyName, string $declaredType, diff --git a/Neos.ContentRepository.NodeAccess/Classes/FlowQueryOperations/PropertyOperation.php b/Neos.ContentRepository.NodeAccess/Classes/FlowQueryOperations/PropertyOperation.php index 1a87d02de6f..126da8cb028 100644 --- a/Neos.ContentRepository.NodeAccess/Classes/FlowQueryOperations/PropertyOperation.php +++ b/Neos.ContentRepository.NodeAccess/Classes/FlowQueryOperations/PropertyOperation.php @@ -20,6 +20,7 @@ use Neos\Eel\FlowQuery\FlowQueryException; use Neos\Eel\FlowQuery\Operations\AbstractOperation; use Neos\Flow\Annotations as Flow; +use Neos\Neos\Utility\NodeTypeWithFallbackProvider; use Neos\Utility\ObjectAccess; /** @@ -56,6 +57,8 @@ class PropertyOperation extends AbstractOperation */ protected $contentRepositoryRegistry; + use NodeTypeWithFallbackProvider; + /** * {@inheritdoc} * @@ -110,11 +113,9 @@ public function evaluate(FlowQuery $flowQuery, array $arguments): mixed return ObjectAccess::getPropertyPath($element, substr($propertyName, 1)); } - $propertyType = $element->nodeType->hasProperty($propertyName) - ? $element->nodeType->getPropertyType($propertyName) - : null; - - if ($propertyType === 'reference') { + // legacy layer for single references which have been migrated dynamically. + // users still expect them to return a single node instead of an array. + if ($this->getNodeType($element)->hasReference($propertyName) && ($element->nodeType->getReferences()[$propertyName]['__legacyPropertyType'] ?? '') === 'reference') { $subgraph = $this->contentRepositoryRegistry->subgraphForNode($element); return ( $subgraph->findReferences( @@ -124,7 +125,7 @@ public function evaluate(FlowQuery $flowQuery, array $arguments): mixed )?->node; } - if ($propertyType === 'references') { + if ($this->getNodeType($element)->hasReference($propertyName)) { $subgraph = $this->contentRepositoryRegistry->subgraphForNode($element); return $subgraph->findReferences( $element->nodeAggregateId, diff --git a/Neos.Neos/Classes/Controller/Service/NodesController.php b/Neos.Neos/Classes/Controller/Service/NodesController.php index 8e88aa12c55..c1a3a6bdd7d 100644 --- a/Neos.Neos/Classes/Controller/Service/NodesController.php +++ b/Neos.Neos/Classes/Controller/Service/NodesController.php @@ -230,6 +230,7 @@ public function showAction(string $identifier, string $workspaceName = 'live', a $this->throwStatus(404); } + // @todo illegal dependency direction. Neos Neos has no business calling the ui $convertedNodeProperties = $this->nodePropertyConverterService->getPropertiesArray($node); array_walk($convertedNodeProperties, function (&$value) { if (is_array($value)) { diff --git a/Neos.Neos/Classes/Service/Mapping/NodeReferenceConverter.php b/Neos.Neos/Classes/Service/Mapping/NodeReferenceConverter.php index aa4625ce7de..4a2970e52cb 100644 --- a/Neos.Neos/Classes/Service/Mapping/NodeReferenceConverter.php +++ b/Neos.Neos/Classes/Service/Mapping/NodeReferenceConverter.php @@ -22,6 +22,7 @@ /** * Converter to convert node references to their identifiers * + * @deprecated todo remove * @Flow\Scope("singleton") */ class NodeReferenceConverter extends AbstractTypeConverter diff --git a/Neos.Neos/Classes/Service/NodeTypeSchemaBuilder.php b/Neos.Neos/Classes/Service/NodeTypeSchemaBuilder.php index 5e251455661..88fba405762 100644 --- a/Neos.Neos/Classes/Service/NodeTypeSchemaBuilder.php +++ b/Neos.Neos/Classes/Service/NodeTypeSchemaBuilder.php @@ -14,11 +14,9 @@ namespace Neos\Neos\Service; -use Neos\ContentRepository\Core\Factory\ContentRepositoryServiceInterface; use Neos\ContentRepository\Core\NodeType\NodeTypeManager; use Neos\ContentRepository\Core\NodeType\NodeType; use Neos\ContentRepository\Core\SharedModel\Node\NodeName; -use Neos\Utility\Arrays; /** * Renders the Node Type Schema in a format the User Interface understands; @@ -68,7 +66,7 @@ public function generateNodeTypeSchema() foreach ($nodeTypes as $nodeTypeName => $nodeType) { if ($nodeType->isAbstract() === false) { $configuration = $nodeType->getFullConfiguration(); - $configuration['properties'] = Arrays::arrayMergeRecursiveOverrule( + $configuration['properties'] = array_merge( $configuration['properties'] ?? [], $configuration['references'] ?? [], ); From 41b909b1d0f6bb58b5720933e8c7c8d9b05fa463 Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Thu, 16 Nov 2023 21:01:54 +0100 Subject: [PATCH 08/23] TASK: Add `NodeReferencesToWrite::createEmpty` to demonstrate use-case --- .../Feature/NodeReferencing/Dto/NodeReferencesToWrite.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeReferencing/Dto/NodeReferencesToWrite.php b/Neos.ContentRepository.Core/Classes/Feature/NodeReferencing/Dto/NodeReferencesToWrite.php index 8ba90b34b43..3fefefbde63 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/NodeReferencing/Dto/NodeReferencesToWrite.php +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeReferencing/Dto/NodeReferencesToWrite.php @@ -58,6 +58,14 @@ public static function fromArray(array $values): self )); } + /** + * Unset all references for this reference name. + */ + public static function createEmpty(): self + { + return new self(); + } + public static function fromNodeAggregateIds(NodeAggregateIds $nodeAggregateIds): self { return new self(...array_map( From f636e2ba44f591b6eb64da02416e66e006945cb3 Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Thu, 16 Nov 2023 21:27:48 +0100 Subject: [PATCH 09/23] TASK: Migrate type:references earlier before Postprocessors That way the post processors are forced to be adjusted to work with the new format and will stop working for reference like property types The inspector.dataTypes.references? configuration was removed as references are special types. The `NodeReferenceConverter` was removed as it is out of use. --- .../Classes/NodeType/NodeType.php | 44 +++++----- .../DefaultPropertyEditorPostprocessor.php | 24 ++++++ .../Mapping/NodeReferenceConverter.php | 80 ------------------- .../Mapping/NodeTypeStringConverter.php | 4 +- Neos.Neos/Configuration/Settings.yaml | 6 -- 5 files changed, 49 insertions(+), 109 deletions(-) delete mode 100644 Neos.Neos/Classes/Service/Mapping/NodeReferenceConverter.php diff --git a/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php b/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php index b1d08b0e875..302008202f2 100644 --- a/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php +++ b/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php @@ -155,6 +155,29 @@ protected function buildFullConfiguration(): array $this->fullConfiguration['childNodes'] = $sorter->toArray(); } + // migrate old property like references to references + $referencesConfiguration = $this->fullConfiguration['references'] ?? []; + foreach ($this->fullConfiguration['properties'] ?? [] as $propertyName => $propertyConfiguration) { + $propertyType = $propertyConfiguration['type'] ?? 'string'; + switch ($propertyType) { + case 'reference': + unset($propertyConfiguration['type']); + $propertyConfiguration['constraints']['maxItems'] = 1; + // @deprecated remove with 10 + // used to ensure that the FlowQuery property operation will return the node directly but not an array of nodes + $propertyConfiguration['__legacyPropertyType'] = 'reference'; + $referencesConfiguration[$propertyName] = $propertyConfiguration; + unset($this->fullConfiguration['properties'][$propertyName]); + break; + case 'references': + unset($propertyConfiguration['type']); + $referencesConfiguration[$propertyName] = $propertyConfiguration; + unset($this->fullConfiguration['properties'][$propertyName]); + break; + } + } + $this->fullConfiguration['references'] = $referencesConfiguration; + return $this->fullConfiguration; } @@ -530,27 +553,6 @@ public function allowsChildNodeType(NodeType $nodeType): bool */ protected function setFullConfiguration(array $fullConfiguration): void { - $referencesConfiguration = $fullConfiguration['references'] ?? []; - foreach ($fullConfiguration['properties'] ?? [] as $propertyName => $propertyConfiguration) { - $propertyType = $propertyConfiguration['type'] ?? 'string'; - switch ($propertyType) { - case 'reference': - unset($propertyConfiguration['type']); - $propertyConfiguration['constraints']['maxItems'] = 1; - // @deprecated remove with 10 - // used to ensure that the FlowQuery property operation will return the node directly but not an array of nodes - $propertyConfiguration['__legacyPropertyType'] = 'reference'; - $referencesConfiguration[$propertyName] = $propertyConfiguration; - unset($fullConfiguration['properties'][$propertyName]); - break; - case 'references': - unset($propertyConfiguration['type']); - $referencesConfiguration[$propertyName] = $propertyConfiguration; - unset($fullConfiguration['properties'][$propertyName]); - break; - } - } - $fullConfiguration['references'] = $referencesConfiguration; $this->fullConfiguration = $fullConfiguration; } } diff --git a/Neos.Neos/Classes/NodeTypePostprocessor/DefaultPropertyEditorPostprocessor.php b/Neos.Neos/Classes/NodeTypePostprocessor/DefaultPropertyEditorPostprocessor.php index 5011dff1227..992d375d48d 100644 --- a/Neos.Neos/Classes/NodeTypePostprocessor/DefaultPropertyEditorPostprocessor.php +++ b/Neos.Neos/Classes/NodeTypePostprocessor/DefaultPropertyEditorPostprocessor.php @@ -48,6 +48,29 @@ class DefaultPropertyEditorPostprocessor implements NodeTypePostprocessorInterfa public function process(NodeType $nodeType, array &$configuration, array $options): void { $nodeTypeName = $nodeType->name->value; + + foreach ($configuration['references'] as $referenceName => &$referenceConfiguration) { + if (!isset($referenceConfiguration['ui']['inspector'])) { + // we presume that these are properties wich are not shown + continue; + } + + $editor = $referenceConfiguration['ui']['inspector']['editor'] ?? null; + + if (!$editor) { + $maxAllowedItems = $referenceConfiguration['constraints']['maxItems'] ?? null; + $editor = $maxAllowedItems === 1 ? 'Neos.Neos/Inspector/Editors/ReferenceEditor' : 'Neos.Neos/Inspector/Editors/ReferencesEditor'; + } + + $mergedInspectorConfiguration = $this->editorDefaultConfiguration[$editor] ?? []; + $mergedInspectorConfiguration = Arrays::arrayMergeRecursiveOverrule( + $mergedInspectorConfiguration, + $referenceConfiguration['ui']['inspector'] + ); + $referenceConfiguration['ui']['inspector'] = $mergedInspectorConfiguration; + $referenceConfiguration['ui']['inspector']['editor'] = $editor; + } + if (isset($configuration['properties']) && is_array($configuration['properties'])) { foreach ($configuration['properties'] as $propertyName => &$propertyConfiguration) { if (!isset($propertyConfiguration['type'])) { @@ -57,6 +80,7 @@ public function process(NodeType $nodeType, array &$configuration, array $option $type = $propertyConfiguration['type']; if (!isset($propertyConfiguration['ui']['inspector'])) { + // we presume that these are properties wich are not shown continue; } diff --git a/Neos.Neos/Classes/Service/Mapping/NodeReferenceConverter.php b/Neos.Neos/Classes/Service/Mapping/NodeReferenceConverter.php deleted file mode 100644 index 4a2970e52cb..00000000000 --- a/Neos.Neos/Classes/Service/Mapping/NodeReferenceConverter.php +++ /dev/null @@ -1,80 +0,0 @@ - - * @api - */ - protected $sourceTypes = [Node::class, 'array']; - - /** - * The target type this converter can convert to. - * - * @var string - * @api - */ - protected $targetType = 'string'; - - /** - * The priority for this converter. - * - * @var integer - * @api - */ - protected $priority = 0; - - /** - * {@inheritdoc} - * - * @param Node|array $source - * @param string $targetType - * @param array $convertedChildProperties - * @param PropertyMappingConfigurationInterface $configuration - * @return string|array the target type - */ - public function convertFrom( - $source, - $targetType, - array $convertedChildProperties = [], - PropertyMappingConfigurationInterface $configuration = null - ) { - if (is_array($source)) { - $result = []; - foreach ($source as $node) { - $result[] = $node->nodeAggregateId->value; - } - } else { - $result = $source->nodeAggregateId->value; - } - - return $result; - } -} diff --git a/Neos.Neos/Classes/Service/Mapping/NodeTypeStringConverter.php b/Neos.Neos/Classes/Service/Mapping/NodeTypeStringConverter.php index faab7553e7d..78f5de2b977 100644 --- a/Neos.Neos/Classes/Service/Mapping/NodeTypeStringConverter.php +++ b/Neos.Neos/Classes/Service/Mapping/NodeTypeStringConverter.php @@ -20,8 +20,8 @@ use Neos\ContentRepository\Core\NodeType\NodeType; /** - * Convert a boolean to a JavaScript compatible string representation. - * + * @internal + * @deprecated todo still used? * @Flow\Scope("singleton") */ class NodeTypeStringConverter extends AbstractTypeConverter diff --git a/Neos.Neos/Configuration/Settings.yaml b/Neos.Neos/Configuration/Settings.yaml index 4f45714082b..2dd3dc68e3b 100755 --- a/Neos.Neos/Configuration/Settings.yaml +++ b/Neos.Neos/Configuration/Settings.yaml @@ -226,12 +226,6 @@ Neos: editor: Neos.Neos/Inspector/Editors/DateTimeEditor editorOptions: format: d-m-Y - reference: - typeConverter: Neos\Neos\Service\Mapping\NodeReferenceConverter - editor: Neos.Neos/Inspector/Editors/ReferenceEditor - references: - typeConverter: Neos\Neos\Service\Mapping\NodeReferenceConverter - editor: Neos.Neos/Inspector/Editors/ReferencesEditor editors: Neos.Neos/Inspector/Editors/CodeEditor: editorOptions: From c1a3f46c798fa5bbac090562b88b8e3c759b0f20 Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Fri, 17 Nov 2023 10:11:37 +0100 Subject: [PATCH 10/23] TASK: Adjust further code to not check `getPropertyType === reference` --- .../Infrastructure/Property/PropertyConverter.php | 2 +- .../Classes/NodeDataToEventsProcessor.php | 5 ++--- .../DefaultPropertyEditorPostprocessorTest.php | 9 +++++++++ 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/Neos.ContentRepository.Core/Classes/Infrastructure/Property/PropertyConverter.php b/Neos.ContentRepository.Core/Classes/Infrastructure/Property/PropertyConverter.php index f546702531f..1d08528b78b 100644 --- a/Neos.ContentRepository.Core/Classes/Infrastructure/Property/PropertyConverter.php +++ b/Neos.ContentRepository.Core/Classes/Infrastructure/Property/PropertyConverter.php @@ -44,7 +44,7 @@ public function serializePropertyValues( $serializedPropertyValues = []; foreach ($propertyValuesToWrite->values as $propertyName => $propertyValue) { - if (!isset($nodeType->getProperties()[$propertyName]) && $propertyValue === null) { + if (!$nodeType->hasProperty($propertyName) && $propertyValue === null) { // The property is undefined and the value null => we want to remove it, so we set it to null $serializedPropertyValues[$propertyName] = null; } else { diff --git a/Neos.ContentRepository.LegacyNodeMigration/Classes/NodeDataToEventsProcessor.php b/Neos.ContentRepository.LegacyNodeMigration/Classes/NodeDataToEventsProcessor.php index cc3ad13609b..1ed444e2e14 100644 --- a/Neos.ContentRepository.LegacyNodeMigration/Classes/NodeDataToEventsProcessor.php +++ b/Neos.ContentRepository.LegacyNodeMigration/Classes/NodeDataToEventsProcessor.php @@ -316,9 +316,7 @@ public function extractPropertyValuesAndReferences(array $nodeDataRow, NodeType } foreach ($decodedProperties as $propertyName => $propertyValue) { - $type = $nodeType->getPropertyType($propertyName); - - if ($type === 'reference' || $type === 'references') { + if ($nodeType->hasReference($propertyName)) { if (!empty($propertyValue)) { if (!is_array($propertyValue)) { $propertyValue = [$propertyValue]; @@ -328,6 +326,7 @@ public function extractPropertyValuesAndReferences(array $nodeDataRow, NodeType continue; } + $type = $nodeType->getPropertyType($propertyName); // In the old `Node`, we call the property mapper to convert the returned properties from NodeData; // so we need to do the same here. try { diff --git a/Neos.Neos/Tests/Unit/NodeTypePostprocessor/DefaultPropertyEditorPostprocessorTest.php b/Neos.Neos/Tests/Unit/NodeTypePostprocessor/DefaultPropertyEditorPostprocessorTest.php index 5d5723b2bec..cb33db54ae0 100644 --- a/Neos.Neos/Tests/Unit/NodeTypePostprocessor/DefaultPropertyEditorPostprocessorTest.php +++ b/Neos.Neos/Tests/Unit/NodeTypePostprocessor/DefaultPropertyEditorPostprocessorTest.php @@ -44,6 +44,7 @@ private function processConfiguration(array $configuration, array $dataTypesDefa public function processConvertsPropertyConfiguration(): void { $configuration = [ + 'references' => [], 'properties' => [ 'propertyWithoutType' => [ 'ui' => [ @@ -154,6 +155,7 @@ public function processConvertsPropertyConfiguration(): void ]; $expectedResult = [ + 'references' => [], 'properties' => [ 'propertyWithoutType' => [ 'ui' => [ @@ -268,6 +270,7 @@ public function processThrowsExceptionIfNoPropertyEditorCanBeResolved(): void $this->expectException(\Neos\Neos\Exception::class); $configuration = [ + 'references' => [], 'properties' => [ 'someProperty' => [ 'type' => 'string', @@ -287,6 +290,8 @@ public function processThrowsExceptionIfNoPropertyEditorCanBeResolved(): void public function processConvertsCreationDialogConfiguration(): void { $configuration = [ + 'references' => [], + 'properties' => [], 'ui' => [ 'creationDialog' => [ 'elements' => [ @@ -377,6 +382,8 @@ public function processConvertsCreationDialogConfiguration(): void ]; $expectedResult = [ + 'references' => [], + 'properties' => [], 'ui' => [ 'creationDialog' => [ 'elements' => [ @@ -472,6 +479,8 @@ public function processConvertsCreationDialogConfiguration(): void public function processDoesNotThrowExceptionIfNoCreationDialogEditorCanBeResolved(): void { $configuration = [ + 'references' => [], + 'properties' => [], 'ui' => [ 'creationDialog' => [ 'elements' => [ From c5b96191ba052eceede4d0d3c6c08b031aea6cee Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Thu, 15 Feb 2024 13:24:05 +0100 Subject: [PATCH 11/23] TASK: Switch back to legacy reference notation to avoid conflicts while merging --- ...SetNodeReferences_ConstraintChecks.feature | 146 ++++++++++-------- ...etNodeReferences_WithoutDimensions.feature | 83 +++++----- ...3-SetNodeReferences_WithDimensions.feature | 17 +- ...4-SetNodeReferences_PropertyScopes.feature | 14 +- ...odeVariation_After_NodeReferencing.feature | 71 ++++----- ...bleNodeAggregate_WithoutDimensions.feature | 5 +- ...isableNodeAggregate_WithDimensions.feature | 5 +- ...bleNodeAggregate_WithoutDimensions.feature | 5 +- ...EnableNodeAggregate_WithDimensions.feature | 5 +- ...oveNodeAggregate_WithoutDimensions.feature | 5 +- ...RemoveNodeAggregate_WithDimensions.feature | 5 +- .../NodeReferencesOnForkContentStream.feature | 25 +-- .../AllCommandsAreImplemented.feature | 20 +-- .../RemoveNodeAggregateAfterDisabling.feature | 5 +- ...dChildNodeConnectedThroughEdgeName.feature | 3 +- .../NodeTraversal/FindNodeById.feature | 3 +- .../NodeTraversal/FindNodeByPath.feature | 3 +- .../NodeTraversal/FindParentNode.feature | 3 +- .../Features/NodeTraversal/References.feature | 3 +- .../NodeTraversal/SiblingNodes.feature | 3 +- .../Features/NodeTraversal/Timestamps.feature | 3 +- 21 files changed, 238 insertions(+), 194 deletions(-) diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/05-NodeReferencing/01-SetNodeReferences_ConstraintChecks.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/05-NodeReferencing/01-SetNodeReferences_ConstraintChecks.feature index 89f7bd32936..2f929fd6952 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/05-NodeReferencing/01-SetNodeReferences_ConstraintChecks.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/05-NodeReferencing/01-SetNodeReferences_ConstraintChecks.feature @@ -10,19 +10,25 @@ Feature: Constraint checks on SetNodeReferences And using the following node types: """yaml 'Neos.ContentRepository.Testing:NodeWithReferences': - properties: - nonReferenceProperty: - type: string references: - reference: + constrainedReferenceCount: constraints: maxItems: 1 - references: {} - constrainedReference: + # legacy notation + properties: + referenceProperty: + type: reference + referencesProperty: + type: references + nonReferenceProperty: + type: string + constrainedReferenceProperty: + type: reference constraints: nodeTypes: 'Neos.ContentRepository.Testing:NodeWithReferences': false - referenceWithProperties: + referencePropertyWithProperties: + type: reference properties: text: type: string @@ -33,159 +39,167 @@ Feature: Constraint checks on SetNodeReferences And I am in content repository "default" And I am user identified by "initiating-user-identifier" And the command CreateRootWorkspace is executed with payload: - | Key | Value | - | workspaceName | "live" | - | workspaceTitle | "Live" | - | workspaceDescription | "The live workspace" | - | newContentStreamId | "cs-identifier" | + | Key | Value | + | workspaceName | "live" | + | workspaceTitle | "Live" | + | workspaceDescription | "The live workspace" | + | newContentStreamId | "cs-identifier" | And the graph projection is fully up to date And I am in content stream "cs-identifier" and dimension space point {"language":"de"} And the command CreateRootNodeAggregateWithNode is executed with payload: - | Key | Value | + | Key | Value | | nodeAggregateId | "lady-eleonode-rootford" | - | nodeTypeName | "Neos.ContentRepository:Root" | + | nodeTypeName | "Neos.ContentRepository:Root" | And the graph projection is fully up to date And the following CreateNodeAggregateWithNode commands are executed: - | nodeAggregateId | nodeTypeName | parentNodeAggregateId | - | source-nodandaise | Neos.ContentRepository.Testing:NodeWithReferences | lady-eleonode-rootford | - | anthony-destinode | Neos.ContentRepository.Testing:NodeWithReferences | lady-eleonode-rootford | - | berta-destinode | Neos.ContentRepository.Testing:NodeWithReferences | lady-eleonode-rootford | + | nodeAggregateId | nodeTypeName | parentNodeAggregateId | + | source-nodandaise | Neos.ContentRepository.Testing:NodeWithReferences | lady-eleonode-rootford | + | anthony-destinode | Neos.ContentRepository.Testing:NodeWithReferences | lady-eleonode-rootford | + | berta-destinode | Neos.ContentRepository.Testing:NodeWithReferences | lady-eleonode-rootford | # checks for contentStreamId Scenario: Try to reference nodes in a non-existent content stream When the command SetNodeReferences is executed with payload and exceptions are caught: - | Key | Value | - | contentStreamId | "i-do-not-exist" | - | sourceNodeAggregateId | "source-nodandaise" | - | referenceName | "reference" | - | references | [{"target":"anthony-destinode"}] | + | Key | Value | + | contentStreamId | "i-do-not-exist" | + | sourceNodeAggregateId | "source-nodandaise" | + | referenceName | "referenceProperty" | + | references | [{"target":"anthony-destinode"}] | Then the last command should have thrown an exception of type "ContentStreamDoesNotExistYet" with code 1521386692 # checks for sourceNodeAggregateId Scenario: Try to reference nodes in a non-existent node aggregate When the command SetNodeReferences is executed with payload and exceptions are caught: - | Key | Value | + | Key | Value | | sourceNodeAggregateId | "i-do-not-exist" | - | referenceName | "reference" | - | references | [{"target":"anthony-destinode"}] | + | referenceName | "referenceProperty" | + | references | [{"target":"anthony-destinode"}] | Then the last command should have thrown an exception of type "NodeAggregateCurrentlyDoesNotExist" with code 1541678486 Scenario: Try to reference nodes in a root node aggregate When the command SetNodeReferences is executed with payload and exceptions are caught: - | Key | Value | + | Key | Value | | sourceNodeAggregateId | "lady-eleonode-rootford" | - | referenceName | "reference" | - | references | [{"target":"anthony-destinode"}] | + | referenceName | "referenceProperty" | + | references | [{"target":"anthony-destinode"}] | Then the last command should have thrown an exception of type "NodeAggregateIsRoot" # checks for sourceOriginDimensionSpacePoint Scenario: Try to reference nodes in an origin dimension space point that does not exist When the command SetNodeReferences is executed with payload and exceptions are caught: | Key | Value | - | sourceNodeAggregateId | "source-nodandaise" | + | sourceNodeAggregateId | "source-nodandaise" | | sourceOriginDimensionSpacePoint | {"undeclared":"undefined"} | - | referenceName | "reference" | + | referenceName | "referenceProperty" | | references | [{"target":"anthony-destinode"}] | Then the last command should have thrown an exception of type "DimensionSpacePointNotFound" with code 1505929456 Scenario: Try to reference nodes in an origin dimension space point the source node aggregate does not occupy When the command SetNodeReferences is executed with payload and exceptions are caught: | Key | Value | - | sourceNodeAggregateId | "source-nodandaise" | + | sourceNodeAggregateId | "source-nodandaise" | | sourceOriginDimensionSpacePoint | {"language":"en"} | - | referenceName | "reference" | + | referenceName | "referenceProperty" | | references | [{"target":"anthony-destinode"}] | Then the last command should have thrown an exception of type "DimensionSpacePointIsNotYetOccupied" with code 1552595396 # checks for destinationnodeAggregateIds Scenario: Try to reference a non-existent node aggregate When the command SetNodeReferences is executed with payload and exceptions are caught: - | Key | Value | + | Key | Value | | sourceNodeAggregateId | "source-nodandaise" | - | referenceName | "reference" | - | references | [{"target":"i-do-not-exist"}] | + | referenceName | "referenceProperty" | + | references | [{"target":"i-do-not-exist"}] | Then the last command should have thrown an exception of type "NodeAggregateCurrentlyDoesNotExist" with code 1541678486 Scenario: Try to reference a root node aggregate When the command SetNodeReferences is executed with payload and exceptions are caught: - | Key | Value | + | Key | Value | | sourceNodeAggregateId | "source-nodandaise" | - | referenceName | "reference" | - | references | [{"target":"lady-eleonode-rootford"}] | + | referenceName | "referenceProperty" | + | references | [{"target":"lady-eleonode-rootford"}] | Then the last command should have thrown an exception of type "NodeAggregateIsRoot" Scenario: Try to set references exceeding the maxItems count When the command SetNodeReferences is executed with payload and exceptions are caught: - | Key | Value | - | sourceNodeAggregateId | "source-nodandaise" | - | referenceName | "reference" | - | references | [{"target":"anthony-destinode"}, {"target":"berta-destinode"}] | + | Key | Value | + | sourceNodeAggregateId | "source-nodandaise" | + | referenceName | "constrainedReferenceCount" | + | references | [{"target":"anthony-destinode"}, {"target":"berta-destinode"}] | + Then the last command should have thrown an exception of type "ReferenceCannotBeSet" with code 1700150156 + + Scenario: Try to set references exceeding the maxItems count for legacy property reference declaration + When the command SetNodeReferences is executed with payload and exceptions are caught: + | Key | Value | + | sourceNodeAggregateId | "source-nodandaise" | + | referenceName | "referenceProperty" | + | references | [{"target":"anthony-destinode"}, {"target":"berta-destinode"}] | Then the last command should have thrown an exception of type "ReferenceCannotBeSet" with code 1700150156 Scenario: Try to reference a node aggregate of a type not matching the constraints When the command SetNodeReferences is executed with payload and exceptions are caught: - | Key | Value | + | Key | Value | | sourceNodeAggregateId | "source-nodandaise" | - | referenceName | "constrainedReference" | - | references | [{"target":"anthony-destinode"}] | + | referenceName | "constrainedReferenceProperty" | + | references | [{"target":"anthony-destinode"}] | Then the last command should have thrown an exception of type "ReferenceCannotBeSet" with code 1648502149 Scenario: Try to reference a node aggregate which does not cover the source origin Given the command CreateNodeAggregateWithNode is executed with payload: - | Key | Value | - | nodeAggregateId | "sir-david-nodenborough" | - | nodeTypeName | "Neos.ContentRepository.Testing:NodeWithReferences" | - | originDimensionSpacePoint | {"language":"en"} | - | parentNodeAggregateId | "lady-eleonode-rootford" | + | Key | Value | + | nodeAggregateId | "sir-david-nodenborough" | + | nodeTypeName | "Neos.ContentRepository.Testing:NodeWithReferences" | + | originDimensionSpacePoint | {"language":"en"} | + | parentNodeAggregateId | "lady-eleonode-rootford" | And the graph projection is fully up to date When the command SetNodeReferences is executed with payload and exceptions are caught: | Key | Value | - | sourceNodeAggregateId | "source-nodandaise" | + | sourceNodeAggregateId | "source-nodandaise" | | sourceOriginDimensionSpacePoint | {"language": "de"} | - | referenceName | "reference" | + | referenceName | "referenceProperty" | | references | [{"target":"sir-david-nodenborough"}] | Then the last command should have thrown an exception of type "NodeAggregateDoesCurrentlyNotCoverDimensionSpacePoint" # checks for referenceName Scenario: Try to reference nodes in an undefined property: When the command SetNodeReferences is executed with payload and exceptions are caught: - | Key | Value | + | Key | Value | | sourceNodeAggregateId | "source-nodandaise" | - | referenceName | "i-do-not-exist" | - | references | [{"target":"anthony-destinode"}] | + | referenceName | "i-do-not-exist" | + | references | [{"target":"anthony-destinode"}] | Then the last command should have thrown an exception of type "ReferenceCannotBeSet" with code 1618670106 Scenario: Try to reference nodes in a property that is not of type reference(s): When the command SetNodeReferences is executed with payload and exceptions are caught: - | Key | Value | + | Key | Value | | sourceNodeAggregateId | "source-nodandaise" | - | referenceName | "nonReferenceProperty" | - | references | [{"target":"anthony-destinode"}] | + | referenceName | "nonReferenceProperty" | + | references | [{"target":"anthony-destinode"}] | Then the last command should have thrown an exception of type "ReferenceCannotBeSet" with code 1618670106 Scenario: Try to reference a node aggregate using a property the reference does not declare When the command SetNodeReferences is executed with payload and exceptions are caught: - | Key | Value | + | Key | Value | | nodeAggregateId | "nody-mc-nodeface" | | sourceNodeAggregateId | "source-nodandaise" | - | referenceName | "referenceWithProperties" | - | references | [{"target":"anthony-destinode", "properties":{"i-do-not-exist": "whatever"}}] | + | referenceName | "referencePropertyWithProperties" | + | references | [{"target":"anthony-destinode", "properties":{"i-do-not-exist": "whatever"}}] | Then the last command should have thrown an exception of type "ReferenceCannotBeSet" with code 1658406662 Scenario: Try to set a property with a value of a wrong type When the command SetNodeReferences is executed with payload and exceptions are caught: - | Key | Value | + | Key | Value | | nodeAggregateId | "nody-mc-nodeface" | | sourceNodeAggregateId | "source-nodandaise" | - | referenceName | "referenceWithProperties" | - | references | [{"target":"anthony-destinode", "properties":{"postalAddress": "28 31st of February Street"}}] | + | referenceName | "referencePropertyWithProperties" | + | references | [{"target":"anthony-destinode", "properties":{"postalAddress": "28 31st of February Street"}}] | Then the last command should have thrown an exception of type "ReferenceCannotBeSet" with code 1658406762 Scenario: Node reference cannot hold multiple targets to the same node When the command SetNodeReferences is executed with payload and exceptions are caught: | Key | Value | | sourceNodeAggregateId | "source-nodandaise" | - | referenceName | "references" | + | referenceName | "referencesProperty" | | references | [{"target":"anthony-destinode"}, {"target":"anthony-destinode"}] | Then the last command should have thrown an exception of type "InvalidArgumentException" with code 1700150910 diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/05-NodeReferencing/02-SetNodeReferences_WithoutDimensions.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/05-NodeReferencing/02-SetNodeReferences_WithoutDimensions.feature index f5729b00e6b..fdd2769f2f5 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/05-NodeReferencing/02-SetNodeReferences_WithoutDimensions.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/05-NodeReferencing/02-SetNodeReferences_WithoutDimensions.feature @@ -8,15 +8,19 @@ Feature: Node References without Dimensions And using the following node types: """yaml 'Neos.ContentRepository.Testing:NodeWithReferences': - references: - reference: {} - references: {} - restrictedReference: + properties: + referenceProperty: + type: reference + referencesProperty: + type: references + restrictedReferenceProperty: + type: reference constraints: nodeTypes: '*': false 'Neos.ContentRepository.Testing:NodeWithReferences': true - referenceWithProperty: + referencePropertyWithProperty: + type: reference properties: text: type: string @@ -24,7 +28,8 @@ Feature: Node References without Dimensions type: Neos\ContentRepository\Core\Tests\Behavior\Fixtures\DayOfWeek postalAddress: type: 'Neos\ContentRepository\Core\Tests\Behavior\Fixtures\PostalAddress' - referencesWithProperty: + referencesPropertyWithProperty: + type: references properties: text: type: string @@ -60,110 +65,110 @@ Feature: Node References without Dimensions When the command SetNodeReferences is executed with payload: | Key | Value | | sourceNodeAggregateId | "source-nodandaise" | - | referenceName | "reference" | + | referenceName | "referenceProperty" | | references | [{"target": "anthony-destinode"}] | And the graph projection is fully up to date Then I expect node aggregate identifier "source-nodandaise" to lead to node cs-identifier;source-nodandaise;{} And I expect this node to have the following references: | Name | Node | Properties | - | reference | cs-identifier;anthony-destinode;{} | null | + | referenceProperty | cs-identifier;anthony-destinode;{} | null | And I expect node aggregate identifier "anthony-destinode" to lead to node cs-identifier;anthony-destinode;{} And I expect this node to be referenced by: | Name | Node | Properties | - | reference | cs-identifier;source-nodandaise;{} | null | + | referenceProperty | cs-identifier;source-nodandaise;{} | null | Scenario: Ensure that a single reference with properties between nodes can be set and read When the command SetNodeReferences is executed with payload: | Key | Value | | sourceNodeAggregateId | "source-nodandaise" | - | referenceName | "referenceWithProperty" | + | referenceName | "referencePropertyWithProperty" | | references | [{"target": "anthony-destinode", "properties":{"text":"my text", "dayOfWeek":"DayOfWeek:https://schema.org/Friday", "postalAddress":"PostalAddress:dummy"}}] | And the graph projection is fully up to date Then I expect node aggregate identifier "source-nodandaise" to lead to node cs-identifier;source-nodandaise;{} And I expect this node to have the following references: | Name | Node | Properties | - | referenceWithProperty | cs-identifier;anthony-destinode;{} | {"text":"my text", "dayOfWeek":"DayOfWeek:https://schema.org/Friday", "postalAddress":"PostalAddress:dummy"} | + | referencePropertyWithProperty | cs-identifier;anthony-destinode;{} | {"text":"my text", "dayOfWeek":"DayOfWeek:https://schema.org/Friday", "postalAddress":"PostalAddress:dummy"} | And I expect node aggregate identifier "anthony-destinode" to lead to node cs-identifier;anthony-destinode;{} And I expect this node to be referenced by: | Name | Node | Properties | - | referenceWithProperty | cs-identifier;source-nodandaise;{} | {"text":"my text", "dayOfWeek":"DayOfWeek:https://schema.org/Friday", "postalAddress":"PostalAddress:dummy"} | + | referencePropertyWithProperty | cs-identifier;source-nodandaise;{} | {"text":"my text", "dayOfWeek":"DayOfWeek:https://schema.org/Friday", "postalAddress":"PostalAddress:dummy"} | Scenario: Ensure that multiple references between nodes can be set and read When the command SetNodeReferences is executed with payload: | Key | Value | | sourceNodeAggregateId | "source-nodandaise" | - | referenceName | "references" | + | referenceName | "referencesProperty" | | references | [{"target": "berta-destinode"}, {"target": "carl-destinode"}] | And the graph projection is fully up to date Then I expect node aggregate identifier "source-nodandaise" to lead to node cs-identifier;source-nodandaise;{} And I expect this node to have the following references: | Name | Node | Properties | - | references | cs-identifier;berta-destinode;{} | null | - | references | cs-identifier;carl-destinode;{} | null | + | referencesProperty | cs-identifier;berta-destinode;{} | null | + | referencesProperty | cs-identifier;carl-destinode;{} | null | And I expect node aggregate identifier "berta-destinode" to lead to node cs-identifier;berta-destinode;{} And I expect this node to be referenced by: | Name | Node | Properties | - | references | cs-identifier;source-nodandaise;{} | null | + | referencesProperty | cs-identifier;source-nodandaise;{} | null | And I expect node aggregate identifier "carl-destinode" to lead to node cs-identifier;carl-destinode;{} And I expect this node to be referenced by: | Name | Node | Properties | - | references | cs-identifier;source-nodandaise;{} | null | + | referencesProperty | cs-identifier;source-nodandaise;{} | null | Scenario: Ensure that multiple references with properties between nodes can be set and read When the command SetNodeReferences is executed with payload: | Key | Value | | sourceNodeAggregateId | "source-nodandaise" | - | referenceName | "referencesWithProperty" | + | referenceName | "referencesPropertyWithProperty" | | references | [{"target":"berta-destinode", "properties":{"text":"my text", "dayOfWeek":"DayOfWeek:https://schema.org/Wednesday", "postalAddress":"PostalAddress:dummy"}}, {"target":"carl-destinode", "properties":{"text":"my other text", "dayOfWeek":"DayOfWeek:https://schema.org/Friday", "postalAddress":"PostalAddress:anotherDummy"}}] | And the graph projection is fully up to date Then I expect node aggregate identifier "source-nodandaise" to lead to node cs-identifier;source-nodandaise;{} And I expect this node to have the following references: | Name | Node | Properties | - | referencesWithProperty | cs-identifier;berta-destinode;{} | {"text":"my text", "dayOfWeek":"DayOfWeek:https://schema.org/Wednesday", "postalAddress":"PostalAddress:dummy"} | - | referencesWithProperty | cs-identifier;carl-destinode;{} | {"text":"my other text", "dayOfWeek":"DayOfWeek:https://schema.org/Friday", "postalAddress":"PostalAddress:anotherDummy"} | + | referencesPropertyWithProperty | cs-identifier;berta-destinode;{} | {"text":"my text", "dayOfWeek":"DayOfWeek:https://schema.org/Wednesday", "postalAddress":"PostalAddress:dummy"} | + | referencesPropertyWithProperty | cs-identifier;carl-destinode;{} | {"text":"my other text", "dayOfWeek":"DayOfWeek:https://schema.org/Friday", "postalAddress":"PostalAddress:anotherDummy"} | And I expect node aggregate identifier "berta-destinode" to lead to node cs-identifier;berta-destinode;{} And I expect this node to be referenced by: | Name | Node | Properties | - | referencesWithProperty | cs-identifier;source-nodandaise;{} | {"text":"my text", "dayOfWeek":"DayOfWeek:https://schema.org/Wednesday", "postalAddress":"PostalAddress:dummy"} | + | referencesPropertyWithProperty | cs-identifier;source-nodandaise;{} | {"text":"my text", "dayOfWeek":"DayOfWeek:https://schema.org/Wednesday", "postalAddress":"PostalAddress:dummy"} | And I expect node aggregate identifier "carl-destinode" to lead to node cs-identifier;carl-destinode;{} And I expect this node to be referenced by: | Name | Node | Properties | - | referencesWithProperty | cs-identifier;source-nodandaise;{} | {"text":"my other text", "dayOfWeek":"DayOfWeek:https://schema.org/Friday", "postalAddress":"PostalAddress:anotherDummy"} | + | referencesPropertyWithProperty | cs-identifier;source-nodandaise;{} | {"text":"my other text", "dayOfWeek":"DayOfWeek:https://schema.org/Friday", "postalAddress":"PostalAddress:anotherDummy"} | Scenario: Ensure that references between nodes can be set and overwritten When the command SetNodeReferences is executed with payload: | Key | Value | | sourceNodeAggregateId | "source-nodandaise" | | references | [{"target": "berta-destinode"}, {"target": "carl-destinode"}] | - | referenceName | "references" | + | referenceName | "referencesProperty" | And the graph projection is fully up to date And the command SetNodeReferences is executed with payload: | Key | Value | | sourceNodeAggregateId | "source-nodandaise" | | references | [{"target": "anthony-destinode"}] | - | referenceName | "references" | + | referenceName | "referencesProperty" | And the graph projection is fully up to date Then I expect node aggregate identifier "source-nodandaise" to lead to node cs-identifier;source-nodandaise;{} And I expect this node to have the following references: | Name | Node | Properties | - | references | cs-identifier;anthony-destinode;{} | null | + | referencesProperty | cs-identifier;anthony-destinode;{} | null | And I expect node aggregate identifier "anthony-destinode" to lead to node cs-identifier;anthony-destinode;{} And I expect this node to be referenced by: | Name | Node | Properties | - | references | cs-identifier;source-nodandaise;{} | null | + | referencesProperty | cs-identifier;source-nodandaise;{} | null | And I expect node aggregate identifier "berta-destinode" to lead to node cs-identifier;berta-destinode;{} And I expect this node to not be referenced @@ -177,21 +182,21 @@ Feature: Node References without Dimensions | Key | Value | | sourceNodeAggregateId | "source-nodandaise" | | references | [{"target": "berta-destinode"}, {"target": "carl-destinode"}] | - | referenceName | "references" | + | referenceName | "referencesProperty" | And the graph projection is fully up to date And the command SetNodeReferences is executed with payload: | Key | Value | | sourceNodeAggregateId | "source-nodandaise" | | references | [{"target": "carl-destinode"}, {"target": "berta-destinode"}] | - | referenceName | "references" | + | referenceName | "referencesProperty" | And the graph projection is fully up to date Then I expect node aggregate identifier "source-nodandaise" to lead to node cs-identifier;source-nodandaise;{} And I expect this node to have the following references: | Name | Node | Properties | - | references | cs-identifier;carl-destinode;{} | null | - | references | cs-identifier;berta-destinode;{} | null | + | referencesProperty | cs-identifier;carl-destinode;{} | null | + | referencesProperty | cs-identifier;berta-destinode;{} | null | Scenario: Ensure that references between nodes can be deleted @@ -199,14 +204,14 @@ Feature: Node References without Dimensions | Key | Value | | sourceNodeAggregateId | "source-nodandaise" | | references | [{"target": "berta-destinode"}, {"target": "carl-destinode"}] | - | referenceName | "references" | + | referenceName | "referencesProperty" | And the graph projection is fully up to date And the command SetNodeReferences is executed with payload: | Key | Value | | sourceNodeAggregateId | "source-nodandaise" | | references | [] | - | referenceName | "references" | + | referenceName | "referencesProperty" | And the graph projection is fully up to date @@ -225,36 +230,36 @@ Feature: Node References without Dimensions | Key | Value | | sourceNodeAggregateId | "source-nodandaise" | | references | [{"target": "anthony-destinode"}] | - | referenceName | "reference" | + | referenceName | "referenceProperty" | And the graph projection is fully up to date And the command SetNodeReferences is executed with payload: | Key | Value | | sourceNodeAggregateId | "berta-destinode" | | references | [{"target": "anthony-destinode"}] | - | referenceName | "reference" | + | referenceName | "referenceProperty" | And the graph projection is fully up to date Then I expect node aggregate identifier "anthony-destinode" to lead to node cs-identifier;anthony-destinode;{} And I expect this node to be referenced by: | Name | Node | Properties | - | reference | cs-identifier;berta-destinode;{} | null | - | reference | cs-identifier;source-nodandaise;{} | null | + | referenceProperty | cs-identifier;berta-destinode;{} | null | + | referenceProperty | cs-identifier;source-nodandaise;{} | null | Scenario: Ensure that a reference between nodes can be set and read when matching constraints When the command SetNodeReferences is executed with payload: | Key | Value | | sourceNodeAggregateId | "source-nodandaise" | | references | [{"target": "anthony-destinode"}] | - | referenceName | "restrictedReference" | + | referenceName | "restrictedReferenceProperty" | And the graph projection is fully up to date Then I expect node aggregate identifier "source-nodandaise" to lead to node cs-identifier;source-nodandaise;{} And I expect this node to have the following references: | Name | Node | Properties | - | restrictedReference | cs-identifier;anthony-destinode;{} | null | + | restrictedReferenceProperty | cs-identifier;anthony-destinode;{} | null | And I expect node aggregate identifier "anthony-destinode" to lead to node cs-identifier;anthony-destinode;{} And I expect this node to be referenced by: | Name | Node | Properties | - | restrictedReference | cs-identifier;source-nodandaise;{} | null | + | restrictedReferenceProperty | cs-identifier;source-nodandaise;{} | null | diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/05-NodeReferencing/03-SetNodeReferences_WithDimensions.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/05-NodeReferencing/03-SetNodeReferences_WithDimensions.feature index bd7620a20b2..ce9a9f8f4f6 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/05-NodeReferencing/03-SetNodeReferences_WithDimensions.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/05-NodeReferencing/03-SetNodeReferences_WithDimensions.feature @@ -13,11 +13,12 @@ Feature: Node References with Dimensions """yaml 'Neos.ContentRepository.Testing:NodeWithReferences': properties: + referenceProperty: + type: reference + referencesProperty: + type: references text: type: string - references: - reference: {} - references: {} """ And using identifier "default", I define a content repository And I am in content repository "default" @@ -44,7 +45,7 @@ Feature: Node References with Dimensions When the command SetNodeReferences is executed with payload: | Key | Value | | sourceNodeAggregateId | "source-nodandaise" | - | referenceName | "reference" | + | referenceName | "referenceProperty" | | references | [{"target": "anthony-destinode"}] | And the graph projection is fully up to date @@ -52,18 +53,18 @@ Feature: Node References with Dimensions Then I expect node aggregate identifier "source-nodandaise" to lead to node cs-identifier;source-nodandaise;{"language": "de"} And I expect this node to have the following references: | Name | Node | Properties | - | reference | cs-identifier;anthony-destinode;{"language": "de"} | null | + | referenceProperty | cs-identifier;anthony-destinode;{"language": "de"} | null | Then I expect node aggregate identifier "anthony-destinode" to lead to node cs-identifier;anthony-destinode;{"language": "de"} And I expect this node to be referenced by: | Name | Node | Properties | - | reference | cs-identifier;source-nodandaise;{"language": "de"} | null | + | referenceProperty | cs-identifier;source-nodandaise;{"language": "de"} | null | When I am in content stream "cs-identifier" and dimension space point {"language": "ch"} Then I expect node aggregate identifier "source-nodandaise" to lead to node cs-identifier;source-nodandaise;{"language": "de"} And I expect this node to have the following references: | Name | Node | Properties | - | reference | cs-identifier;anthony-destinode;{"language": "de"} | null | + | referenceProperty | cs-identifier;anthony-destinode;{"language": "de"} | null | Then I expect node aggregate identifier "anthony-destinode" to lead to node cs-identifier;anthony-destinode;{"language": "de"} And I expect this node to be referenced by: | Name | Node | Properties | - | reference | cs-identifier;source-nodandaise;{"language": "de"} | null | + | referenceProperty | cs-identifier;source-nodandaise;{"language": "de"} | null | diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/05-NodeReferencing/04-SetNodeReferences_PropertyScopes.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/05-NodeReferencing/04-SetNodeReferences_PropertyScopes.feature index 7718b6bcdaa..b5be6986e06 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/05-NodeReferencing/04-SetNodeReferences_PropertyScopes.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/05-NodeReferencing/04-SetNodeReferences_PropertyScopes.feature @@ -10,20 +10,28 @@ Feature: Set node properties with different scopes And using the following node types: """yaml 'Neos.ContentRepository.Testing:NodeWithReferences': - references: - unscopedReference: {} - unscopedReferences: {} + properties: + unscopedReference: + type: reference + unscopedReferences: + type: references nodeScopedReference: + type: reference scope: node nodeScopedReferences: + type: references scope: node nodeAggregateScopedReference: + type: reference scope: nodeAggregate nodeAggregateScopedReferences: + type: references scope: nodeAggregate specializationsScopedReference: + type: reference scope: specializations specializationsScopedReferences: + type: references scope: specializations """ And using identifier "default", I define a content repository diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/05-NodeReferencing/05-NodeVariation_After_NodeReferencing.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/05-NodeReferencing/05-NodeVariation_After_NodeReferencing.feature index da3381d70f7..ae2e995690a 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/05-NodeReferencing/05-NodeVariation_After_NodeReferencing.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/05-NodeReferencing/05-NodeVariation_After_NodeReferencing.feature @@ -13,11 +13,12 @@ Feature: Node References with Dimensions """yaml 'Neos.ContentRepository.Testing:NodeWithReferences': properties: + referenceProperty: + type: reference + referencesProperty: + type: references text: type: string - references: - reference: {} - references: {} """ And using identifier "default", I define a content repository And I am in content repository "default" @@ -44,7 +45,7 @@ Feature: Node References with Dimensions When the command SetNodeReferences is executed with payload: | Key | Value | | sourceNodeAggregateId | "source-nodandaise" | - | referenceName | "reference" | + | referenceName | "referenceProperty" | | references | [{"target": "anthony-destinode"}] | And the graph projection is fully up to date @@ -60,30 +61,30 @@ Feature: Node References with Dimensions Then I expect node aggregate identifier "source-nodandaise" to lead to node cs-identifier;source-nodandaise;{"language": "ch"} And I expect this node to have the following references: | Name | Node | Properties | - | reference | cs-identifier;anthony-destinode;{"language": "de"} | null | + | referenceProperty | cs-identifier;anthony-destinode;{"language": "de"} | null | Then I expect node aggregate identifier "anthony-destinode" to lead to node cs-identifier;anthony-destinode;{"language": "de"} And I expect this node to be referenced by: | Name | Node | Properties | - | reference | cs-identifier;source-nodandaise;{"language": "ch"} | null | + | referenceProperty | cs-identifier;source-nodandaise;{"language": "ch"} | null | # the reference must also exist on the non-touched nodes When I am in content stream "cs-identifier" and dimension space point {"language": "de"} Then I expect node aggregate identifier "source-nodandaise" to lead to node cs-identifier;source-nodandaise;{"language": "de"} And I expect this node to have the following references: | Name | Node | Properties | - | reference | cs-identifier;anthony-destinode;{"language": "de"} | null | + | referenceProperty | cs-identifier;anthony-destinode;{"language": "de"} | null | And I expect node aggregate identifier "anthony-destinode" to lead to node cs-identifier;anthony-destinode;{"language": "de"} And I expect this node to be referenced by: | Name | Node | Properties | - | reference | cs-identifier;source-nodandaise;{"language": "de"} | null | + | referenceProperty | cs-identifier;source-nodandaise;{"language": "de"} | null | # now, when modifying the specialization reference, only the specialization is changed. When the command SetNodeReferences is executed with payload: | Key | Value | | sourceNodeAggregateId | "source-nodandaise" | | sourceOriginDimensionSpacePoint | {"language": "ch"} | - | referenceName | "reference" | + | referenceName | "referenceProperty" | | references | [{"target": "source-nodandaise"}] | And the graph projection is fully up to date @@ -92,21 +93,21 @@ Feature: Node References with Dimensions Then I expect node aggregate identifier "source-nodandaise" to lead to node cs-identifier;source-nodandaise;{"language": "ch"} And I expect this node to have the following references: | Name | Node | Properties | - | reference | cs-identifier;source-nodandaise;{"language": "ch"} | null | + | referenceProperty | cs-identifier;source-nodandaise;{"language": "ch"} | null | And I expect this node to be referenced by: | Name | Node | Properties | - | reference | cs-identifier;source-nodandaise;{"language": "ch"} | null | + | referenceProperty | cs-identifier;source-nodandaise;{"language": "ch"} | null | # unmodified on the untouched nodes When I am in content stream "cs-identifier" and dimension space point {"language": "de"} Then I expect node aggregate identifier "source-nodandaise" to lead to node cs-identifier;source-nodandaise;{"language": "de"} And I expect this node to have the following references: | Name | Node | Properties | - | reference | cs-identifier;anthony-destinode;{"language": "de"} | null | + | referenceProperty | cs-identifier;anthony-destinode;{"language": "de"} | null | Then I expect node aggregate identifier "anthony-destinode" to lead to node cs-identifier;anthony-destinode;{"language": "de"} And I expect this node to be referenced by: | Name | Node | Properties | - | reference | cs-identifier;source-nodandaise;{"language": "de"} | null | + | referenceProperty | cs-identifier;source-nodandaise;{"language": "de"} | null | Scenario: specialize the source node, only set reference on the specialization. Then, the reference should only appear on the specialization When the command CreateNodeVariant is executed with payload: @@ -120,7 +121,7 @@ Feature: Node References with Dimensions | Key | Value | | sourceNodeAggregateId | "source-nodandaise" | | sourceOriginDimensionSpacePoint | {"language": "ch"} | - | referenceName | "reference" | + | referenceName | "referenceProperty" | | references | [{"target": "anthony-destinode"}] | And the graph projection is fully up to date @@ -130,11 +131,11 @@ Feature: Node References with Dimensions Then I expect node aggregate identifier "source-nodandaise" to lead to node cs-identifier;source-nodandaise;{"language": "ch"} And I expect this node to have the following references: | Name | Node | Properties | - | reference | cs-identifier;anthony-destinode;{"language": "de"} | null | + | referenceProperty | cs-identifier;anthony-destinode;{"language": "de"} | null | Then I expect node aggregate identifier "anthony-destinode" to lead to node cs-identifier;anthony-destinode;{"language": "de"} And I expect this node to be referenced by: | Name | Node | Properties | - | reference | cs-identifier;source-nodandaise;{"language": "ch"} | null | + | referenceProperty | cs-identifier;source-nodandaise;{"language": "ch"} | null | # on the other nodes, the reference does not exist. When I am in content stream "cs-identifier" and dimension space point {"language": "de"} @@ -157,7 +158,7 @@ Feature: Node References with Dimensions When the command SetNodeReferences is executed with payload: | Key | Value | | sourceNodeAggregateId | "source-nodandaise" | - | referenceName | "reference" | + | referenceName | "referenceProperty" | | references | [{"target": "anthony-destinode"}] | And the graph projection is fully up to date @@ -173,32 +174,32 @@ Feature: Node References with Dimensions Then I expect node aggregate identifier "source-nodandaise" to lead to node cs-identifier;source-nodandaise;{"language": "en"} And I expect this node to have the following references: | Name | Node | Properties | - | reference | cs-identifier;anthony-destinode;{"language": "en"} | null | + | referenceProperty | cs-identifier;anthony-destinode;{"language": "en"} | null | Then I expect node aggregate identifier "anthony-destinode" to lead to node cs-identifier;anthony-destinode;{"language": "en"} And I expect this node to be referenced by: | Name | Node | Properties | - | reference | cs-identifier;source-nodandaise;{"language": "en"} | null | + | referenceProperty | cs-identifier;source-nodandaise;{"language": "en"} | null | # the reference must also exist on the non-touched nodes When I am in content stream "cs-identifier" and dimension space point {"language": "de"} Then I expect node aggregate identifier "source-nodandaise" to lead to node cs-identifier;source-nodandaise;{"language": "de"} And I expect this node to have the following references: | Name | Node | Properties | - | reference | cs-identifier;anthony-destinode;{"language": "de"} | null | + | referenceProperty | cs-identifier;anthony-destinode;{"language": "de"} | null | Then I expect node aggregate identifier "anthony-destinode" to lead to node cs-identifier;anthony-destinode;{"language": "de"} And I expect this node to be referenced by: | Name | Node | Properties | - | reference | cs-identifier;source-nodandaise;{"language": "de"} | null | + | referenceProperty | cs-identifier;source-nodandaise;{"language": "de"} | null | When I am in content stream "cs-identifier" and dimension space point {"language": "ch"} Then I expect node aggregate identifier "source-nodandaise" to lead to node cs-identifier;source-nodandaise;{"language": "de"} And I expect this node to have the following references: | Name | Node | Properties | - | reference | cs-identifier;anthony-destinode;{"language": "de"} | null | + | referenceProperty | cs-identifier;anthony-destinode;{"language": "de"} | null | Then I expect node aggregate identifier "anthony-destinode" to lead to node cs-identifier;anthony-destinode;{"language": "de"} And I expect this node to be referenced by: | Name | Node | Properties | - | reference | cs-identifier;source-nodandaise;{"language": "de"} | null | + | referenceProperty | cs-identifier;source-nodandaise;{"language": "de"} | null | # now, when modifying the peer reference, only the peer is changed. @@ -206,7 +207,7 @@ Feature: Node References with Dimensions | Key | Value | | sourceNodeAggregateId | "source-nodandaise" | | sourceOriginDimensionSpacePoint | {"language": "en"} | - | referenceName | "reference" | + | referenceName | "referenceProperty" | | references | [{"target": "source-nodandaise"}] | And the graph projection is fully up to date @@ -215,31 +216,31 @@ Feature: Node References with Dimensions Then I expect node aggregate identifier "source-nodandaise" to lead to node cs-identifier;source-nodandaise;{"language": "en"} And I expect this node to have the following references: | Name | Node | Properties | - | reference | cs-identifier;source-nodandaise;{"language": "en"} | null | + | referenceProperty | cs-identifier;source-nodandaise;{"language": "en"} | null | And I expect this node to be referenced by: | Name | Node | Properties | - | reference | cs-identifier;source-nodandaise;{"language": "en"} | null | + | referenceProperty | cs-identifier;source-nodandaise;{"language": "en"} | null | # unmodified on the untouched nodes When I am in content stream "cs-identifier" and dimension space point {"language": "de"} Then I expect node aggregate identifier "source-nodandaise" to lead to node cs-identifier;source-nodandaise;{"language": "de"} And I expect this node to have the following references: | Name | Node | Properties | - | reference | cs-identifier;anthony-destinode;{"language": "de"} | null | + | referenceProperty | cs-identifier;anthony-destinode;{"language": "de"} | null | Then I expect node aggregate identifier "anthony-destinode" to lead to node cs-identifier;anthony-destinode;{"language": "de"} And I expect this node to be referenced by: | Name | Node | Properties | - | reference | cs-identifier;source-nodandaise;{"language": "de"} | null | + | referenceProperty | cs-identifier;source-nodandaise;{"language": "de"} | null | When I am in content stream "cs-identifier" and dimension space point {"language": "ch"} Then I expect node aggregate identifier "source-nodandaise" to lead to node cs-identifier;source-nodandaise;{"language": "de"} And I expect this node to have the following references: | Name | Node | Properties | - | reference | cs-identifier;anthony-destinode;{"language": "de"} | null | + | referenceProperty | cs-identifier;anthony-destinode;{"language": "de"} | null | Then I expect node aggregate identifier "anthony-destinode" to lead to node cs-identifier;anthony-destinode;{"language": "de"} And I expect this node to be referenced by: | Name | Node | Properties | - | reference | cs-identifier;source-nodandaise;{"language": "de"} | null | + | referenceProperty | cs-identifier;source-nodandaise;{"language": "de"} | null | Scenario: Create a reference, then create a generalization of the source node; and the references should exist on the generalization # We need to create a new ch-only node to test this; as by default, only a german node already exists shining through in ch @@ -255,7 +256,7 @@ Feature: Node References with Dimensions | Key | Value | | sourceNodeAggregateId | "ch-only" | | sourceOriginDimensionSpacePoint | {"language": "ch"} | - | referenceName | "reference" | + | referenceName | "referenceProperty" | | references | [{"target": "anthony-destinode"}] | And the graph projection is fully up to date @@ -272,20 +273,20 @@ Feature: Node References with Dimensions Then I expect node aggregate identifier "ch-only" to lead to node cs-identifier;ch-only;{"language": "de"} Then I expect this node to have the following references: | Name | Node | Properties | - | reference | cs-identifier;anthony-destinode;{"language": "de"} | null | + | referenceProperty | cs-identifier;anthony-destinode;{"language": "de"} | null | Then I expect node aggregate identifier "anthony-destinode" to lead to node cs-identifier;anthony-destinode;{"language": "de"} And I expect this node to be referenced by: | Name | Node | Properties | - | reference | cs-identifier;ch-only;{"language": "de"} | null | + | referenceProperty | cs-identifier;ch-only;{"language": "de"} | null | # the reference must also exist on the non-touched node When I am in content stream "cs-identifier" and dimension space point {"language": "ch"} Then I expect node aggregate identifier "ch-only" to lead to node cs-identifier;ch-only;{"language": "ch"} Then I expect this node to have the following references: | Name | Node | Properties | - | reference | cs-identifier;anthony-destinode;{"language": "de"} | null | + | referenceProperty | cs-identifier;anthony-destinode;{"language": "de"} | null | Then I expect node aggregate identifier "anthony-destinode" to lead to node cs-identifier;anthony-destinode;{"language": "de"} And I expect this node to be referenced by: | Name | Node | Properties | - | reference | cs-identifier;ch-only;{"language": "ch"} | null | + | referenceProperty | cs-identifier;ch-only;{"language": "ch"} | null | diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/06-NodeDisabling/02-DisableNodeAggregate_WithoutDimensions.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/06-NodeDisabling/02-DisableNodeAggregate_WithoutDimensions.feature index a55e0051b6e..9f8d73c1cf0 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/06-NodeDisabling/02-DisableNodeAggregate_WithoutDimensions.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/06-NodeDisabling/02-DisableNodeAggregate_WithoutDimensions.feature @@ -10,8 +10,9 @@ Feature: Disable a node aggregate And using the following node types: """yaml 'Neos.ContentRepository.Testing:Document': - references: - references: {} + properties: + references: + type: references """ And using identifier "default", I define a content repository And I am in content repository "default" diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/06-NodeDisabling/03-DisableNodeAggregate_WithDimensions.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/06-NodeDisabling/03-DisableNodeAggregate_WithDimensions.feature index 3547f672333..9ab8ece1340 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/06-NodeDisabling/03-DisableNodeAggregate_WithDimensions.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/06-NodeDisabling/03-DisableNodeAggregate_WithDimensions.feature @@ -12,8 +12,9 @@ Feature: Disable a node aggregate And using the following node types: """yaml 'Neos.ContentRepository.Testing:Document': - references: - references: {} + properties: + references: + type: references """ And using identifier "default", I define a content repository And I am in content repository "default" diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/06-NodeDisabling/05-EnableNodeAggregate_WithoutDimensions.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/06-NodeDisabling/05-EnableNodeAggregate_WithoutDimensions.feature index 3ef4e8e5984..1a53bc1d35b 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/06-NodeDisabling/05-EnableNodeAggregate_WithoutDimensions.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/06-NodeDisabling/05-EnableNodeAggregate_WithoutDimensions.feature @@ -10,8 +10,9 @@ Feature: Enable a node aggregate And using the following node types: """yaml 'Neos.ContentRepository.Testing:Document': - references: - references: {} + properties: + references: + type: references """ And using identifier "default", I define a content repository And I am in content repository "default" diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/06-NodeDisabling/06-EnableNodeAggregate_WithDimensions.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/06-NodeDisabling/06-EnableNodeAggregate_WithDimensions.feature index 3d130f013e0..eef0ffed0ed 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/06-NodeDisabling/06-EnableNodeAggregate_WithDimensions.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/06-NodeDisabling/06-EnableNodeAggregate_WithDimensions.feature @@ -12,8 +12,9 @@ Feature: Enable a node aggregate And using the following node types: """yaml 'Neos.ContentRepository.Testing:Document': - references: - references: {} + properties: + references: + type: references """ And using identifier "default", I define a content repository And I am in content repository "default" diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/07-NodeRemoval/02-RemoveNodeAggregate_WithoutDimensions.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/07-NodeRemoval/02-RemoveNodeAggregate_WithoutDimensions.feature index 6dbe73de5a1..093aa669a7a 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/07-NodeRemoval/02-RemoveNodeAggregate_WithoutDimensions.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/07-NodeRemoval/02-RemoveNodeAggregate_WithoutDimensions.feature @@ -10,8 +10,9 @@ Feature: Remove NodeAggregate And using the following node types: """yaml 'Neos.ContentRepository.Testing:Document': - references: - references: {} + properties: + references: + type: references """ And using identifier "default", I define a content repository And I am in content repository "default" diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/07-NodeRemoval/03-RemoveNodeAggregate_WithDimensions.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/07-NodeRemoval/03-RemoveNodeAggregate_WithDimensions.feature index edfc254c7d3..1186753cc69 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/07-NodeRemoval/03-RemoveNodeAggregate_WithDimensions.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/07-NodeRemoval/03-RemoveNodeAggregate_WithDimensions.feature @@ -12,8 +12,9 @@ Feature: Remove NodeAggregate And using the following node types: """yaml 'Neos.ContentRepository.Testing:Document': - references: - references: {} + properties: + references: + type: references """ And using identifier "default", I define a content repository And I am in content repository "default" diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/ContentStreamForking/NodeReferencesOnForkContentStream.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/ContentStreamForking/NodeReferencesOnForkContentStream.feature index 7fb2db3b3f9..e16bb97b991 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/ContentStreamForking/NodeReferencesOnForkContentStream.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/ContentStreamForking/NodeReferencesOnForkContentStream.feature @@ -12,11 +12,12 @@ Feature: On forking a content stream, node references should be copied as well. """yaml 'Neos.ContentRepository.Testing:NodeWithReferences': properties: + referenceProperty: + type: reference + referencesProperty: + type: references text: type: string - references: - reference: {} - references: {} """ And using identifier "default", I define a content repository And I am in content repository "default" @@ -43,7 +44,7 @@ Feature: On forking a content stream, node references should be copied as well. Given the command SetNodeReferences is executed with payload: | Key | Value | | sourceNodeAggregateId | "source-nodandaise" | - | referenceName | "reference" | + | referenceName | "referenceProperty" | | references | [{"target": "anthony-destinode"}] | And the graph projection is fully up to date @@ -58,21 +59,21 @@ Feature: On forking a content stream, node references should be copied as well. Then I expect node aggregate identifier "source-nodandaise" to lead to node user-cs-identifier;source-nodandaise;{"language": "de"} Then I expect this node to have the following references: | Name | Node | Properties | - | reference | user-cs-identifier;anthony-destinode;{"language": "de"} | null | + | referenceProperty | user-cs-identifier;anthony-destinode;{"language": "de"} | null | Then I expect node aggregate identifier "anthony-destinode" to lead to node user-cs-identifier;anthony-destinode;{"language": "de"} And I expect this node to be referenced by: | Name | Node | Properties | - | reference | user-cs-identifier;source-nodandaise;{"language": "de"} | null | + | referenceProperty | user-cs-identifier;source-nodandaise;{"language": "de"} | null | When I am in content stream "user-cs-identifier" and dimension space point {"language": "ch"} Then I expect node aggregate identifier "source-nodandaise" to lead to node user-cs-identifier;source-nodandaise;{"language": "de"} Then I expect this node to have the following references: | Name | Node | Properties | - | reference | user-cs-identifier;anthony-destinode;{"language": "de"} | null | + | referenceProperty | user-cs-identifier;anthony-destinode;{"language": "de"} | null | Then I expect node aggregate identifier "anthony-destinode" to lead to node user-cs-identifier;anthony-destinode;{"language": "de"} And I expect this node to be referenced by: | Name | Node | Properties | - | reference | user-cs-identifier;source-nodandaise;{"language": "de"} | null | + | referenceProperty | user-cs-identifier;source-nodandaise;{"language": "de"} | null | # after then modifying the node's properties (thus triggering copy-on-write), the reference property # should still exist (this was a BUG) @@ -86,18 +87,18 @@ Feature: On forking a content stream, node references should be copied as well. Then I expect node aggregate identifier "source-nodandaise" to lead to node user-cs-identifier;source-nodandaise;{"language": "de"} And I expect this node to have the following references: | Name | Node | Properties | - | reference | user-cs-identifier;anthony-destinode;{"language": "de"} | null | + | referenceProperty | user-cs-identifier;anthony-destinode;{"language": "de"} | null | Then I expect node aggregate identifier "anthony-destinode" to lead to node user-cs-identifier;anthony-destinode;{"language": "de"} And I expect this node to be referenced by: | Name | Node | Properties | - | reference | user-cs-identifier;source-nodandaise;{"language": "de"} | null | + | referenceProperty | user-cs-identifier;source-nodandaise;{"language": "de"} | null | When I am in content stream "user-cs-identifier" and dimension space point {"language": "ch"} Then I expect node aggregate identifier "source-nodandaise" to lead to node user-cs-identifier;source-nodandaise;{"language": "de"} And I expect this node to have the following references: | Name | Node | Properties | - | reference | user-cs-identifier;anthony-destinode;{"language": "de"} | null | + | referenceProperty | user-cs-identifier;anthony-destinode;{"language": "de"} | null | Then I expect node aggregate identifier "anthony-destinode" to lead to node user-cs-identifier;anthony-destinode;{"language": "de"} And I expect this node to be referenced by: | Name | Node | Properties | - | reference | user-cs-identifier;source-nodandaise;{"language": "de"} | null | + | referenceProperty | user-cs-identifier;source-nodandaise;{"language": "de"} | null | diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodePublishing/AllCommandsAreImplemented.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodePublishing/AllCommandsAreImplemented.feature index 1116c08314a..6cab17a2920 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodePublishing/AllCommandsAreImplemented.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodePublishing/AllCommandsAreImplemented.feature @@ -25,8 +25,8 @@ Feature: Publishing hide/show scenario of nodes properties: text: type: string - references: - reference: {} + referenceProperty: + type: reference 'Neos.ContentRepository.Testing:Image': properties: image: @@ -309,14 +309,14 @@ Feature: Publishing hide/show scenario of nodes | contentStreamId | "user-cs-identifier" | | sourceNodeAggregateId | "sir-david-nodenborough" | | sourceOriginDimensionSpacePoint | {} | - | referenceName | "reference" | + | referenceName | "referenceProperty" | | references | [{"target":"sir-nodeward-nodington-iii"}] | And the command SetNodeReferences is executed with payload: | Key | Value | | contentStreamId | "user-cs-identifier" | | sourceNodeAggregateId | "nody-mc-nodeface" | | sourceOriginDimensionSpacePoint | {} | - | referenceName | "reference" | + | referenceName | "referenceProperty" | | references | [{"target":"sir-nodeward-nodington-iii"}] | And the graph projection is fully up to date @@ -331,29 +331,29 @@ Feature: Publishing hide/show scenario of nodes Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{} And I expect this node to have the following references: | Name | Node | Properties | - | reference | cs-identifier;sir-nodeward-nodington-iii;{} | null | + | referenceProperty | cs-identifier;sir-nodeward-nodington-iii;{} | null | Then I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{} And I expect this node to have no references Then I expect node aggregate identifier "sir-nodeward-nodington-iii" to lead to node cs-identifier;sir-nodeward-nodington-iii;{} And I expect this node to have no references And I expect this node to be referenced by: | Name | Node | Properties | - | reference | cs-identifier;sir-david-nodenborough;{} | null | + | referenceProperty | cs-identifier;sir-david-nodenborough;{} | null | When I am in the active content stream of workspace "user-test" and dimension space point {} Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node user-cs-identifier-modified;sir-david-nodenborough;{} And I expect this node to have the following references: | Name | Node | Properties | - | reference | user-cs-identifier-modified;sir-nodeward-nodington-iii;{} | null | + | referenceProperty | user-cs-identifier-modified;sir-nodeward-nodington-iii;{} | null | Then I expect node aggregate identifier "nody-mc-nodeface" to lead to node user-cs-identifier-modified;nody-mc-nodeface;{} And I expect this node to have the following references: | Name | Node | Properties | - | reference | user-cs-identifier-modified;sir-nodeward-nodington-iii;{} | null | + | referenceProperty | user-cs-identifier-modified;sir-nodeward-nodington-iii;{} | null | Then I expect node aggregate identifier "sir-nodeward-nodington-iii" to lead to node user-cs-identifier-modified;sir-nodeward-nodington-iii;{} And I expect this node to be referenced by: | Name | Node | Properties | - | reference | user-cs-identifier-modified;nody-mc-nodeface;{} | null | - | reference | user-cs-identifier-modified;sir-david-nodenborough;{} | null | + | referenceProperty | user-cs-identifier-modified;nody-mc-nodeface;{} | null | + | referenceProperty | user-cs-identifier-modified;sir-david-nodenborough;{} | null | Scenario: (CreateNodeAggregateWithNode) It is possible to publish new nodes Given the command CreateWorkspace is executed with payload: diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeRemoval/RemoveNodeAggregateAfterDisabling.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeRemoval/RemoveNodeAggregateAfterDisabling.feature index 976381d072b..16d55fe52d5 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeRemoval/RemoveNodeAggregateAfterDisabling.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeRemoval/RemoveNodeAggregateAfterDisabling.feature @@ -10,8 +10,9 @@ Feature: Disable a node aggregate And using the following node types: """yaml 'Neos.ContentRepository.Testing:Document': - references: - references: {} + properties: + references: + type: references """ And using identifier "default", I define a content repository And I am in content repository "default" diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/FindChildNodeConnectedThroughEdgeName.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/FindChildNodeConnectedThroughEdgeName.feature index 04e1b88be0c..67898ef2130 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/FindChildNodeConnectedThroughEdgeName.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/FindChildNodeConnectedThroughEdgeName.feature @@ -12,12 +12,13 @@ Feature: Find nodes using the findChildNodeConnectedThroughEdgeName query properties: text: type: string - references: refs: + type: references properties: foo: type: string ref: + type: reference properties: foo: type: string diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/FindNodeById.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/FindNodeById.feature index 1b6c20f9ccd..86070abf48c 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/FindNodeById.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/FindNodeById.feature @@ -12,12 +12,13 @@ Feature: Find nodes using the findNodeById query properties: text: type: string - references: refs: + type: references properties: foo: type: string ref: + type: reference properties: foo: type: string diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/FindNodeByPath.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/FindNodeByPath.feature index 0d05f0a073d..22ca151cffa 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/FindNodeByPath.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/FindNodeByPath.feature @@ -15,12 +15,13 @@ Feature: Find nodes using the findNodeByPath query properties: text: type: string - references: refs: + type: references properties: foo: type: string ref: + type: reference properties: foo: type: string diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/FindParentNode.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/FindParentNode.feature index 59749eebf38..bcae519c37d 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/FindParentNode.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/FindParentNode.feature @@ -12,12 +12,13 @@ Feature: Find nodes using the findParentNodes query properties: text: type: string - references: refs: + type: references properties: foo: type: string ref: + type: reference properties: foo: type: string diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/References.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/References.feature index 7eedf29198e..d6befee25cf 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/References.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/References.feature @@ -22,12 +22,13 @@ Feature: Find and count references and their target nodes using the findReferenc type: integer dateProperty: type: DateTime - references: refs: + type: references properties: foo: type: string ref: + type: reference properties: foo: type: string diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/SiblingNodes.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/SiblingNodes.feature index aa3b0beb376..d3d40abfec4 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/SiblingNodes.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/SiblingNodes.feature @@ -12,12 +12,13 @@ Feature: Find sibling nodes using the findPrecedingSiblingNodes and findSucceedi properties: text: type: string - references: refs: + type: references properties: foo: type: string ref: + type: reference properties: foo: type: string diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/Timestamps.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/Timestamps.feature index d9577684313..ddb1b31533e 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/Timestamps.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/Timestamps.feature @@ -14,12 +14,13 @@ Feature: Behavior of Node timestamp properties "created", "originalCreated", "la properties: text: type: string - references: refs: + type: references properties: foo: type: string ref: + type: reference properties: foo: type: string From ee7cea6261b492326faaa78a2e8eca4006cce9b4 Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Thu, 15 Feb 2024 19:50:33 +0100 Subject: [PATCH 12/23] TASK: A NodeType must have distinct names for properties and references --- .../Classes/NodeType/NodeType.php | 6 ++- .../Tests/Unit/NodeType/NodeTypeTest.php | 49 ++++++++++++++++++- 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php b/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php index 0225a35b243..68e0e6f5193 100644 --- a/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php +++ b/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php @@ -15,6 +15,7 @@ */ use Neos\ContentRepository\Core\NodeType\Exception\TetheredNodeNotConfigured; +use Neos\ContentRepository\Core\SharedModel\Exception\NodeConfigurationException; use Neos\ContentRepository\Core\SharedModel\Node\NodeName; use Neos\Utility\ObjectAccess; use Neos\Utility\Arrays; @@ -77,7 +78,7 @@ class NodeType /** * @param NodeTypeName $name Name of the node type - * @param array $declaredSuperTypes Parent types of this node type + * @param array $declaredSuperTypes Parent types instances of this node type, if null it should be unset * @param array $configuration the configuration for this node type which is defined in the schema * @throws \InvalidArgumentException * @@ -158,6 +159,9 @@ protected function buildFullConfiguration(): array // migrate old property like references to references $referencesConfiguration = $this->fullConfiguration['references'] ?? []; foreach ($this->fullConfiguration['properties'] ?? [] as $propertyName => $propertyConfiguration) { + if (isset($this->fullConfiguration['references'][$propertyName])) { + throw new NodeConfigurationException(sprintf('NodeType %s cannot declare "%s" as property and reference.', $this->name->value, $propertyName), 1708022344); + } $propertyType = $propertyConfiguration['type'] ?? 'string'; switch ($propertyType) { case 'reference': diff --git a/Neos.ContentRepository.Core/Tests/Unit/NodeType/NodeTypeTest.php b/Neos.ContentRepository.Core/Tests/Unit/NodeType/NodeTypeTest.php index 3777e0c725c..66f45f49066 100644 --- a/Neos.ContentRepository.Core/Tests/Unit/NodeType/NodeTypeTest.php +++ b/Neos.ContentRepository.Core/Tests/Unit/NodeType/NodeTypeTest.php @@ -15,7 +15,7 @@ use Neos\ContentRepository\Core\NodeType\DefaultNodeLabelGeneratorFactory; use Neos\ContentRepository\Core\NodeType\NodeTypeName; use Neos\ContentRepository\Core\NodeType\NodeType; -use Neos\ContentRepository\Core\NodeType\NodeTypeManager; +use Neos\ContentRepository\Core\SharedModel\Exception\NodeConfigurationException; use PHPUnit\Framework\TestCase; /** @@ -139,6 +139,7 @@ public function aNodeTypeHasAName() public function setDeclaredSuperTypesExpectsAnArrayOfNodeTypesAsKeys() { $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionCode(1291300950); new NodeType(NodeTypeName::fromString('ContentRepository:Folder'), ['foo' => true], [], new DefaultNodeLabelGeneratorFactory() ); } @@ -149,9 +150,54 @@ public function setDeclaredSuperTypesExpectsAnArrayOfNodeTypesAsKeys() public function setDeclaredSuperTypesAcceptsAnArrayOfNodeTypes() { $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionCode(1291300950); new NodeType(NodeTypeName::fromString('ContentRepository:Folder'), ['foo'], [], new DefaultNodeLabelGeneratorFactory()); } + /** + * @test + */ + public function aNodeTypeMustHaveDistinctNamesForPropertiesReferences() + { + $nodeType = new NodeType(NodeTypeName::fromString('ContentRepository:Invalid'), [], [ + 'properties' => [ + 'foo' => [ + 'type' => 'string', + ] + ], + 'references' => [ + 'foo' => [] + ] + ], new DefaultNodeLabelGeneratorFactory()); + $this->expectException(NodeConfigurationException::class); + $this->expectExceptionCode(1708022344); + // initialize the node type + $nodeType->getFullConfiguration(); + } + + /** + * @test + */ + public function aNodeTypeMustHaveDistinctNamesForPropertiesReferencesInInheritance() + { + $superNodeType = new NodeType(NodeTypeName::fromString('ContentRepository:Super'), [], [ + 'properties' => [ + 'foo' => [ + 'type' => 'string', + ] + ] + ], new DefaultNodeLabelGeneratorFactory()); + $nodeType = new NodeType(NodeTypeName::fromString('ContentRepository:Invalid'), ['ContentRepository:Super' => $superNodeType], [ + 'references' => [ + 'foo' => [] + ] + ], new DefaultNodeLabelGeneratorFactory()); + $this->expectException(NodeConfigurationException::class); + $this->expectExceptionCode(1708022344); + // initialize the node type + $nodeType->getFullConfiguration(); + } + /** * @test */ @@ -377,6 +423,7 @@ protected function getNodeType(string $nodeTypeName): ?NodeType $configuration = $this->nodeTypesFixture[$nodeTypeName]; $declaredSuperTypes = []; + // duplicated from the node type manager if (isset($configuration['superTypes']) && is_array($configuration['superTypes'])) { foreach ($configuration['superTypes'] as $superTypeName => $enabled) { $declaredSuperTypes[$superTypeName] = $enabled === true ? $this->getNodeType($superTypeName) : null; From 95a447c9ca974fc8b168517afbfe0628905b8880 Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Thu, 15 Feb 2024 20:39:34 +0100 Subject: [PATCH 13/23] TASK: Add NodeType unit tests for new functionality --- .../Classes/NodeType/NodeType.php | 2 +- .../Unit/NodeType/NodeTypeManagerTest.php | 2 +- .../Tests/Unit/NodeType/NodeTypeTest.php | 179 +++++++++++++++++- 3 files changed, 179 insertions(+), 4 deletions(-) diff --git a/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php b/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php index 68e0e6f5193..957b1fc83f9 100644 --- a/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php +++ b/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php @@ -473,7 +473,7 @@ public function getPropertyType(string $propertyName): string if (!$this->hasProperty($propertyName)) { throw new \InvalidArgumentException( sprintf('NodeType schema has no property "%s" configured for the NodeType "%s". Cannot read its type.', $propertyName, $this->name->value), - 1695062252040 + 1708025421 ); } diff --git a/Neos.ContentRepository.Core/Tests/Unit/NodeType/NodeTypeManagerTest.php b/Neos.ContentRepository.Core/Tests/Unit/NodeType/NodeTypeManagerTest.php index ea4e62c9365..f2bb2ff12af 100644 --- a/Neos.ContentRepository.Core/Tests/Unit/NodeType/NodeTypeManagerTest.php +++ b/Neos.ContentRepository.Core/Tests/Unit/NodeType/NodeTypeManagerTest.php @@ -398,7 +398,7 @@ public function allInheritedNodeTypePropertiesCannotBeUnset(): void /** * @test */ - public function anInheritedNodeTypePropertyCannotBeSetToEmptyArray(): void + public function anInheritedNodeTypePropertyCanBeOverruledWithEmptyArray(): void { $nodeTypesFixture = [ 'Neos.ContentRepository.Testing:Base' => [ diff --git a/Neos.ContentRepository.Core/Tests/Unit/NodeType/NodeTypeTest.php b/Neos.ContentRepository.Core/Tests/Unit/NodeType/NodeTypeTest.php index 66f45f49066..1078f68a4a5 100644 --- a/Neos.ContentRepository.Core/Tests/Unit/NodeType/NodeTypeTest.php +++ b/Neos.ContentRepository.Core/Tests/Unit/NodeType/NodeTypeTest.php @@ -140,8 +140,7 @@ public function setDeclaredSuperTypesExpectsAnArrayOfNodeTypesAsKeys() { $this->expectException(\InvalidArgumentException::class); $this->expectExceptionCode(1291300950); - new NodeType(NodeTypeName::fromString('ContentRepository:Folder'), ['foo' => true], [], new DefaultNodeLabelGeneratorFactory() - ); + new NodeType(NodeTypeName::fromString('ContentRepository:Folder'), ['foo' => true], [], new DefaultNodeLabelGeneratorFactory()); } /** @@ -412,6 +411,171 @@ public function superTypesRemovedByInheritanceCanBeAddedAgain() self::assertSame($expectedProperties, $nodeType->getProperties()); } + /** + * @test + */ + public function propertyDeclaration() + { + $nodeType = new NodeType(NodeTypeName::fromString('ContentRepository:Node'), [], [ + 'properties' => [ + 'someProperty' => [ + 'type' => 'bool', + 'defaultValue' => false + ] + ] + ], new DefaultNodeLabelGeneratorFactory()); + self::assertTrue($nodeType->hasProperty('someProperty')); + self::assertFalse($nodeType->hasReference('someProperty')); + self::assertSame('bool', $nodeType->getPropertyType('someProperty')); + self::assertEmpty($nodeType->getReferences()); + self::assertNull($nodeType->getConfiguration('references.someProperty')); + self::assertNotNull($nodeType->getConfiguration('properties.someProperty')); + self::assertSame(['someProperty' => false], $nodeType->getDefaultValuesForProperties()); + self::assertSame( + [ + 'someProperty' => [ + 'type' => 'bool', + 'defaultValue' => false + ] + ], + $nodeType->getProperties() + ); + } + + /** + * @test + */ + public function getPropertyTypeThrowsOnInvalidProperty() + { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionCode(1708025421); + $nodeType = new NodeType(NodeTypeName::fromString('ContentRepository:Node'), [], [], new DefaultNodeLabelGeneratorFactory()); + $nodeType->getPropertyType('nonExistent'); + self::assertSame('string', $nodeType->getPropertyType('nonExistent')); + } + + /** + * @test + */ + public function getPropertyTypeFallback() + { + $nodeType = new NodeType(NodeTypeName::fromString('ContentRepository:Node'), [], [ + 'properties' => [ + 'someProperty' => [] + ] + ], new DefaultNodeLabelGeneratorFactory()); + self::assertSame('string', $nodeType->getPropertyType('someProperty')); + } + + /** + * @test + */ + public function getDefaultValuesForPropertiesIgnoresNullAndUnset() + { + $nodeType = new NodeType(NodeTypeName::fromString('ContentRepository:Node'), [], [ + 'properties' => [ + 'someProperty' => [ + 'type' => 'string', + 'defaultValue' => 'lol' + ], + 'otherProperty' => [ + 'type' => 'string', + 'defaultValue' => null + ], + 'thirdProperty' => [ + 'type' => 'string' + ] + ] + ], new DefaultNodeLabelGeneratorFactory()); + self::assertSame(['someProperty' => 'lol'], $nodeType->getDefaultValuesForProperties()); + } + + /** + * @test + */ + public function referencesDeclaration() + { + $nodeType = new NodeType(NodeTypeName::fromString('ContentRepository:Node'), [], [ + 'references' => [ + 'someReferences' => [] + ] + ], new DefaultNodeLabelGeneratorFactory()); + self::assertFalse($nodeType->hasProperty('someReferences')); + self::assertTrue($nodeType->hasReference('someReferences')); + self::assertThrows(fn() => $nodeType->getPropertyType('someReferences'), \InvalidArgumentException::class); + self::assertEmpty($nodeType->getProperties()); + self::assertEmpty($nodeType->getDefaultValuesForProperties()); + self::assertNull($nodeType->getConfiguration('properties.someReferences')); + self::assertNotNull($nodeType->getConfiguration('references.someReferences')); + self::assertSame( + [ + 'someReferences' => [] + ], + $nodeType->getReferences() + ); + } + + /** + * @test + */ + public function legacyPropertyReferenceDeclaration() + { + $nodeType = new NodeType(NodeTypeName::fromString('ContentRepository:Node'), [], [ + 'properties' => [ + 'referenceProperty' => [ + 'type' => 'reference', + ] + ] + ], new DefaultNodeLabelGeneratorFactory()); + // will be available as _real_ reference + self::assertFalse($nodeType->hasProperty('referenceProperty')); + self::assertTrue($nodeType->hasReference('referenceProperty')); + self::assertThrows(fn() => $nodeType->getPropertyType('referenceProperty'), \InvalidArgumentException::class); + self::assertEmpty($nodeType->getProperties()); + self::assertEmpty($nodeType->getDefaultValuesForProperties()); + self::assertNull($nodeType->getConfiguration('properties.referenceProperty')); + self::assertNotNull($nodeType->getConfiguration('references.referenceProperty')); + self::assertSame( + [ + 'referenceProperty' => [ + 'constraints' => [ + 'maxItems' => 1 + ], + '__legacyPropertyType' => 'reference' + ] + ], + $nodeType->getReferences() + ); + } + + /** + * @test + */ + public function legacyPropertyReferencesDeclaration() + { + $nodeType = new NodeType(NodeTypeName::fromString('ContentRepository:Node'), [], [ + 'properties' => [ + 'referencesProperty' => [ + 'type' => 'references', + ] + ] + ], new DefaultNodeLabelGeneratorFactory()); + // will be available as _real_ reference + self::assertFalse($nodeType->hasProperty('referencesProperty')); + self::assertTrue($nodeType->hasReference('referencesProperty')); + self::assertThrows(fn() => $nodeType->getPropertyType('referencesProperty'), \InvalidArgumentException::class); + self::assertEmpty($nodeType->getProperties()); + self::assertEmpty($nodeType->getDefaultValuesForProperties()); + self::assertNull($nodeType->getConfiguration('properties.referencesProperty')); + self::assertNotNull($nodeType->getConfiguration('references.referencesProperty')); + self::assertSame( + [ + 'referencesProperty' => [] + ], + $nodeType->getReferences() + ); + } + /** * Return a nodetype built from the nodeTypesFixture */ @@ -437,4 +601,15 @@ protected function getNodeType(string $nodeTypeName): ?NodeType new DefaultNodeLabelGeneratorFactory() ); } + + private static function assertThrows(callable $fn, string $exceptionClassName): void + { + try { + $fn(); + } catch (\Throwable $e) { + self::assertInstanceOf($exceptionClassName, $e); + return; + } + self::fail('$fn should throw.'); + } } From 454e7309bd420f0b7aa9ad1345c77342726bc172 Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Thu, 15 Feb 2024 21:15:38 +0100 Subject: [PATCH 14/23] TASK: Limit new feature `constraints.maxItems` to new syntax --- .../Classes/NodeType/NodeType.php | 45 ++++++++++++------- .../Tests/Unit/NodeType/NodeTypeTest.php | 20 +++++++++ 2 files changed, 48 insertions(+), 17 deletions(-) diff --git a/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php b/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php index 957b1fc83f9..d2b6b65363c 100644 --- a/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php +++ b/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php @@ -156,28 +156,39 @@ protected function buildFullConfiguration(): array $this->fullConfiguration['childNodes'] = $sorter->toArray(); } - // migrate old property like references to references $referencesConfiguration = $this->fullConfiguration['references'] ?? []; foreach ($this->fullConfiguration['properties'] ?? [] as $propertyName => $propertyConfiguration) { + // assert that references and properties never declare a thing with the same name if (isset($this->fullConfiguration['references'][$propertyName])) { throw new NodeConfigurationException(sprintf('NodeType %s cannot declare "%s" as property and reference.', $this->name->value, $propertyName), 1708022344); } - $propertyType = $propertyConfiguration['type'] ?? 'string'; - switch ($propertyType) { - case 'reference': - unset($propertyConfiguration['type']); - $propertyConfiguration['constraints']['maxItems'] = 1; - // @deprecated remove with 10 - // used to ensure that the FlowQuery property operation will return the node directly but not an array of nodes - $propertyConfiguration['__legacyPropertyType'] = 'reference'; - $referencesConfiguration[$propertyName] = $propertyConfiguration; - unset($this->fullConfiguration['properties'][$propertyName]); - break; - case 'references': - unset($propertyConfiguration['type']); - $referencesConfiguration[$propertyName] = $propertyConfiguration; - unset($this->fullConfiguration['properties'][$propertyName]); - break; + // migrate old property like references to references + $propertyType = $propertyConfiguration['type'] ?? null; + if ($propertyType !== 'reference' && $propertyType !== 'references') { + continue; + } + if (isset($propertyConfiguration['constraints'])) { + // we don't allow the new syntax `constraints.maxItems` on legacy property-like reference-declarations + throw new NodeConfigurationException(sprintf( + 'Legacy property-like reference-declaration for "%s" does not allow new configuration `constraints` in NodeType %s.' + . ' Please use the reference declaration syntax.', + $propertyName, + $this->name->value + ), 1708022344); + } + if ($propertyType === 'reference') { + unset($propertyConfiguration['type']); + $propertyConfiguration['constraints']['maxItems'] = 1; + // @deprecated with Neos 9 + // used to ensure that the FlowQuery property operation will return the node directly but not an array of nodes + $propertyConfiguration['__legacyPropertyType'] = 'reference'; + $referencesConfiguration[$propertyName] = $propertyConfiguration; + unset($this->fullConfiguration['properties'][$propertyName]); + } + if ($propertyType === 'references') { + unset($propertyConfiguration['type']); + $referencesConfiguration[$propertyName] = $propertyConfiguration; + unset($this->fullConfiguration['properties'][$propertyName]); } } $this->fullConfiguration['references'] = $referencesConfiguration; diff --git a/Neos.ContentRepository.Core/Tests/Unit/NodeType/NodeTypeTest.php b/Neos.ContentRepository.Core/Tests/Unit/NodeType/NodeTypeTest.php index 1078f68a4a5..d3b0e076248 100644 --- a/Neos.ContentRepository.Core/Tests/Unit/NodeType/NodeTypeTest.php +++ b/Neos.ContentRepository.Core/Tests/Unit/NodeType/NodeTypeTest.php @@ -576,6 +576,26 @@ public function legacyPropertyReferencesDeclaration() ); } + /** + * @test + */ + public function legacyPropertyReferencesDeclarationShouldNotUseNewFeatures() + { + $nodeType = new NodeType(NodeTypeName::fromString('ContentRepository:Node'), [], [ + 'properties' => [ + 'referencesProperty' => [ + 'type' => 'references', + 'constraints' => [ + 'maxItems' => 1 + ], + ] + ] + ], new DefaultNodeLabelGeneratorFactory()); + $this->expectException(NodeConfigurationException::class); + $this->expectExceptionCode(1708022344); + $nodeType->getReferences(); + } + /** * Return a nodetype built from the nodeTypesFixture */ From 86edf44245aee2d0d27227947af9c96251b32432 Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Thu, 15 Feb 2024 21:45:22 +0100 Subject: [PATCH 15/23] TASK: Limit new feature `properties` to new `references` syntax --- ...SetNodeReferences_ConstraintChecks.feature | 11 ++++----- ...etNodeReferences_WithoutDimensions.feature | 7 +++--- .../NodeTraversal/FindNodeById.feature | 5 ++-- .../NodeTraversal/FindNodeByPath.feature | 5 ++-- .../FindNodeByPathAsNodeName.feature | 5 ++-- .../NodeTraversal/FindParentNode.feature | 5 ++-- .../Features/NodeTraversal/References.feature | 5 ++-- .../NodeTraversal/SiblingNodes.feature | 5 ++-- .../Features/NodeTraversal/Timestamps.feature | 5 ++-- .../Classes/NodeType/NodeType.php | 6 ++--- .../Tests/Unit/NodeType/NodeTypeTest.php | 24 ++++++++++++++++++- 11 files changed, 56 insertions(+), 27 deletions(-) diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/05-NodeReferencing/01-SetNodeReferences_ConstraintChecks.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/05-NodeReferencing/01-SetNodeReferences_ConstraintChecks.feature index 76addd5f2df..11e945e76b4 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/05-NodeReferencing/01-SetNodeReferences_ConstraintChecks.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/05-NodeReferencing/01-SetNodeReferences_ConstraintChecks.feature @@ -12,10 +12,6 @@ Feature: Constraint checks on SetNodeReferences 'Neos.ContentRepository.Testing:ReferencedNode': [] 'Neos.ContentRepository.Testing:NodeWithReferences': - references: - constrainedReferenceCount: - constraints: - maxItems: 1 # legacy notation properties: referenceProperty: @@ -24,13 +20,16 @@ Feature: Constraint checks on SetNodeReferences type: references nonReferenceProperty: type: string + references: + constrainedReferenceCount: + constraints: + maxItems: 1 constrainedReferenceProperty: - type: reference constraints: + maxItems: 1 nodeTypes: 'Neos.ContentRepository.Testing:ReferencedNode': false referencePropertyWithProperties: - type: reference properties: text: type: string diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/05-NodeReferencing/02-SetNodeReferences_WithoutDimensions.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/05-NodeReferencing/02-SetNodeReferences_WithoutDimensions.feature index a89cc2a8bf0..a6b43136abc 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/05-NodeReferencing/02-SetNodeReferences_WithoutDimensions.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/05-NodeReferencing/02-SetNodeReferences_WithoutDimensions.feature @@ -10,19 +10,21 @@ Feature: Node References without Dimensions 'Neos.ContentRepository.Testing:ReferencedNode': [] 'Neos.ContentRepository.Testing:NodeWithReferences': + # legacy notation properties: referenceProperty: type: reference referencesProperty: type: references + references: restrictedReferenceProperty: - type: reference constraints: nodeTypes: '*': false 'Neos.ContentRepository.Testing:ReferencedNode': true referencePropertyWithProperty: - type: reference + constraints: + maxItems: 1 properties: text: type: string @@ -31,7 +33,6 @@ Feature: Node References without Dimensions postalAddress: type: 'Neos\ContentRepository\Core\Tests\Behavior\Fixtures\PostalAddress' referencesPropertyWithProperty: - type: references properties: text: type: string diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/FindNodeById.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/FindNodeById.feature index 86070abf48c..de59f28ed9c 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/FindNodeById.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/FindNodeById.feature @@ -12,13 +12,14 @@ Feature: Find nodes using the findNodeById query properties: text: type: string + references: refs: - type: references properties: foo: type: string ref: - type: reference + constraints: + maxItems: 1 properties: foo: type: string diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/FindNodeByPath.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/FindNodeByPath.feature index 22ca151cffa..0976958e911 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/FindNodeByPath.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/FindNodeByPath.feature @@ -15,13 +15,14 @@ Feature: Find nodes using the findNodeByPath query properties: text: type: string + references: refs: - type: references properties: foo: type: string ref: - type: reference + constraints: + maxItems: 1 properties: foo: type: string diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/FindNodeByPathAsNodeName.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/FindNodeByPathAsNodeName.feature index 9125fd17850..608b268003b 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/FindNodeByPathAsNodeName.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/FindNodeByPathAsNodeName.feature @@ -12,13 +12,14 @@ Feature: Find nodes using the findNodeByPath query with node name as path argume properties: text: type: string + references: refs: - type: references properties: foo: type: string ref: - type: reference + constraints: + maxItems: 1 properties: foo: type: string diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/FindParentNode.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/FindParentNode.feature index bcae519c37d..45cfa67ded9 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/FindParentNode.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/FindParentNode.feature @@ -12,13 +12,14 @@ Feature: Find nodes using the findParentNodes query properties: text: type: string + references: refs: - type: references properties: foo: type: string ref: - type: reference + constraints: + maxItems: 1 properties: foo: type: string diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/References.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/References.feature index d6befee25cf..311559670bd 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/References.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/References.feature @@ -22,13 +22,14 @@ Feature: Find and count references and their target nodes using the findReferenc type: integer dateProperty: type: DateTime + references: refs: - type: references properties: foo: type: string ref: - type: reference + constraints: + maxItems: 1 properties: foo: type: string diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/SiblingNodes.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/SiblingNodes.feature index d3d40abfec4..49fe3546fc3 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/SiblingNodes.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/SiblingNodes.feature @@ -12,13 +12,14 @@ Feature: Find sibling nodes using the findPrecedingSiblingNodes and findSucceedi properties: text: type: string + references: refs: - type: references properties: foo: type: string ref: - type: reference + constraints: + maxItems: 1 properties: foo: type: string diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/Timestamps.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/Timestamps.feature index ddb1b31533e..8a88448faea 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/Timestamps.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/Timestamps.feature @@ -14,13 +14,14 @@ Feature: Behavior of Node timestamp properties "created", "originalCreated", "la properties: text: type: string + references: refs: - type: references properties: foo: type: string ref: - type: reference + constraints: + maxItems: 1 properties: foo: type: string diff --git a/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php b/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php index d2b6b65363c..cb52fc9c4d2 100644 --- a/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php +++ b/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php @@ -167,11 +167,11 @@ protected function buildFullConfiguration(): array if ($propertyType !== 'reference' && $propertyType !== 'references') { continue; } - if (isset($propertyConfiguration['constraints'])) { + if (isset($propertyConfiguration['constraints']) || isset($propertyConfiguration['properties'])) { // we don't allow the new syntax `constraints.maxItems` on legacy property-like reference-declarations throw new NodeConfigurationException(sprintf( - 'Legacy property-like reference-declaration for "%s" does not allow new configuration `constraints` in NodeType %s.' - . ' Please use the reference declaration syntax.', + 'Legacy property-like reference-declaration for "%s" does not allow new configuration `constraints` or `properties` in NodeType %s.' + . ' Please use the reference declaration syntax instead.', $propertyName, $this->name->value ), 1708022344); diff --git a/Neos.ContentRepository.Core/Tests/Unit/NodeType/NodeTypeTest.php b/Neos.ContentRepository.Core/Tests/Unit/NodeType/NodeTypeTest.php index d3b0e076248..36761f7d164 100644 --- a/Neos.ContentRepository.Core/Tests/Unit/NodeType/NodeTypeTest.php +++ b/Neos.ContentRepository.Core/Tests/Unit/NodeType/NodeTypeTest.php @@ -579,7 +579,7 @@ public function legacyPropertyReferencesDeclaration() /** * @test */ - public function legacyPropertyReferencesDeclarationShouldNotUseNewFeatures() + public function legacyPropertyReferencesDeclarationMustNotUseConstraintFeatures() { $nodeType = new NodeType(NodeTypeName::fromString('ContentRepository:Node'), [], [ 'properties' => [ @@ -596,6 +596,28 @@ public function legacyPropertyReferencesDeclarationShouldNotUseNewFeatures() $nodeType->getReferences(); } + /** + * @test + */ + public function legacyPropertyReferencesDeclarationMustNotUsePropertiesFeatures() + { + $nodeType = new NodeType(NodeTypeName::fromString('ContentRepository:Node'), [], [ + 'properties' => [ + 'referencesProperty' => [ + 'type' => 'references', + 'properties' => [ + 'text' => [ + 'type' => 'string' + ] + ], + ] + ] + ], new DefaultNodeLabelGeneratorFactory()); + $this->expectException(NodeConfigurationException::class); + $this->expectExceptionCode(1708022344); + $nodeType->getReferences(); + } + /** * Return a nodetype built from the nodeTypesFixture */ From d028517f3f6240ad0e0f4b29adef0205fda3165d Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Thu, 15 Feb 2024 22:00:22 +0100 Subject: [PATCH 16/23] TASK: Remove unnecessary check in NodeType that is done by phpstan The constructor is @internal and thus we dont have to validate it too strongly. --- .../Classes/NodeType/NodeType.php | 9 --------- .../Classes/NodeType/NodeTypeManager.php | 4 ++-- .../Tests/Unit/NodeType/NodeTypeTest.php | 20 ------------------- 3 files changed, 2 insertions(+), 31 deletions(-) diff --git a/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php b/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php index cb52fc9c4d2..fb195bcc380 100644 --- a/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php +++ b/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php @@ -91,15 +91,6 @@ public function __construct( private readonly NodeLabelGeneratorFactoryInterface $nodeLabelGeneratorFactory ) { $this->name = $name; - - foreach ($declaredSuperTypes as $type) { - if ($type !== null && !$type instanceof NodeType) { - throw new \InvalidArgumentException( - '$declaredSuperTypes must be an array of NodeType objects', - 1291300950 - ); - } - } $this->declaredSuperTypes = $declaredSuperTypes; if (isset($configuration['abstract']) && $configuration['abstract'] === true) { diff --git a/Neos.ContentRepository.Core/Classes/NodeType/NodeTypeManager.php b/Neos.ContentRepository.Core/Classes/NodeType/NodeTypeManager.php index 8c9aac94b17..8b7b6ae50f4 100644 --- a/Neos.ContentRepository.Core/Classes/NodeType/NodeTypeManager.php +++ b/Neos.ContentRepository.Core/Classes/NodeType/NodeTypeManager.php @@ -340,11 +340,11 @@ protected function loadNodeType(string $nodeTypeName, array &$completeNodeTypeCo * * @param array $superTypesConfiguration * @param array $completeNodeTypeConfiguration - * @return array + * @return array */ protected function evaluateSuperTypesConfiguration( array $superTypesConfiguration, - array &$completeNodeTypeConfiguration + array $completeNodeTypeConfiguration ): array { $superTypes = []; foreach ($superTypesConfiguration as $superTypeName => $enabled) { diff --git a/Neos.ContentRepository.Core/Tests/Unit/NodeType/NodeTypeTest.php b/Neos.ContentRepository.Core/Tests/Unit/NodeType/NodeTypeTest.php index 36761f7d164..b4916948fb5 100644 --- a/Neos.ContentRepository.Core/Tests/Unit/NodeType/NodeTypeTest.php +++ b/Neos.ContentRepository.Core/Tests/Unit/NodeType/NodeTypeTest.php @@ -133,26 +133,6 @@ public function aNodeTypeHasAName() self::assertSame('Neos.ContentRepository.Testing:Text', $nodeType->name->value); } - /** - * @test - */ - public function setDeclaredSuperTypesExpectsAnArrayOfNodeTypesAsKeys() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionCode(1291300950); - new NodeType(NodeTypeName::fromString('ContentRepository:Folder'), ['foo' => true], [], new DefaultNodeLabelGeneratorFactory()); - } - - /** - * @test - */ - public function setDeclaredSuperTypesAcceptsAnArrayOfNodeTypes() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionCode(1291300950); - new NodeType(NodeTypeName::fromString('ContentRepository:Folder'), ['foo'], [], new DefaultNodeLabelGeneratorFactory()); - } - /** * @test */ From e2068eaf690d921f884c79fc9139cbe8deec7668 Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Thu, 15 Feb 2024 22:04:09 +0100 Subject: [PATCH 17/23] TASK: Make NodeType `final` --- Neos.ContentRepository.Core/Classes/NodeType/NodeType.php | 2 +- .../NodeTypePostprocessor/CreationDialogPostprocessorTest.php | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php b/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php index fb195bcc380..8f1b844ef1e 100644 --- a/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php +++ b/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php @@ -31,7 +31,7 @@ * * @api Note: The constructor is not part of the public API */ -class NodeType +final class NodeType { /** * Name of this node type. Example: "ContentRepository:Folder" diff --git a/Neos.Neos/Tests/Unit/Domain/NodeTypePostprocessor/CreationDialogPostprocessorTest.php b/Neos.Neos/Tests/Unit/Domain/NodeTypePostprocessor/CreationDialogPostprocessorTest.php index 98dc811c43c..2c34d9d57bb 100644 --- a/Neos.Neos/Tests/Unit/Domain/NodeTypePostprocessor/CreationDialogPostprocessorTest.php +++ b/Neos.Neos/Tests/Unit/Domain/NodeTypePostprocessor/CreationDialogPostprocessorTest.php @@ -1,7 +1,9 @@ creationDialogPostprocessor = new CreationDialogPostprocessor(); - $this->mockNodeType = $this->getMockBuilder(NodeType::class)->disableOriginalConstructor()->getMock(); + $this->mockNodeType = new NodeType(NodeTypeName::fromString('Neos.Neos:Lol'), [], [], new DefaultNodeLabelGeneratorFactory()); } /** From 47433d00b4b1e1d63735ca911e29e0a5196098c4 Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Fri, 16 Feb 2024 14:56:24 +0100 Subject: [PATCH 18/23] TASK: PropertyOperation treat all 'constraints.maxItems' === 1 equally as single reference and return the first (only) node instead of an array --- .../Classes/NodeType/NodeType.php | 3 --- .../Tests/Unit/NodeType/NodeTypeTest.php | 3 +-- .../FlowQueryOperations/PropertyOperation.php | 24 +++++++++---------- 3 files changed, 12 insertions(+), 18 deletions(-) diff --git a/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php b/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php index 8f1b844ef1e..51a5d46f7bb 100644 --- a/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php +++ b/Neos.ContentRepository.Core/Classes/NodeType/NodeType.php @@ -170,9 +170,6 @@ protected function buildFullConfiguration(): array if ($propertyType === 'reference') { unset($propertyConfiguration['type']); $propertyConfiguration['constraints']['maxItems'] = 1; - // @deprecated with Neos 9 - // used to ensure that the FlowQuery property operation will return the node directly but not an array of nodes - $propertyConfiguration['__legacyPropertyType'] = 'reference'; $referencesConfiguration[$propertyName] = $propertyConfiguration; unset($this->fullConfiguration['properties'][$propertyName]); } diff --git a/Neos.ContentRepository.Core/Tests/Unit/NodeType/NodeTypeTest.php b/Neos.ContentRepository.Core/Tests/Unit/NodeType/NodeTypeTest.php index b4916948fb5..adfad98c39e 100644 --- a/Neos.ContentRepository.Core/Tests/Unit/NodeType/NodeTypeTest.php +++ b/Neos.ContentRepository.Core/Tests/Unit/NodeType/NodeTypeTest.php @@ -520,8 +520,7 @@ public function legacyPropertyReferenceDeclaration() 'referenceProperty' => [ 'constraints' => [ 'maxItems' => 1 - ], - '__legacyPropertyType' => 'reference' + ] ] ], $nodeType->getReferences() diff --git a/Neos.ContentRepository.NodeAccess/Classes/FlowQueryOperations/PropertyOperation.php b/Neos.ContentRepository.NodeAccess/Classes/FlowQueryOperations/PropertyOperation.php index 126da8cb028..9c797d0436b 100644 --- a/Neos.ContentRepository.NodeAccess/Classes/FlowQueryOperations/PropertyOperation.php +++ b/Neos.ContentRepository.NodeAccess/Classes/FlowQueryOperations/PropertyOperation.php @@ -113,24 +113,22 @@ public function evaluate(FlowQuery $flowQuery, array $arguments): mixed return ObjectAccess::getPropertyPath($element, substr($propertyName, 1)); } - // legacy layer for single references which have been migrated dynamically. - // users still expect them to return a single node instead of an array. - if ($this->getNodeType($element)->hasReference($propertyName) && ($element->nodeType->getReferences()[$propertyName]['__legacyPropertyType'] ?? '') === 'reference') { - $subgraph = $this->contentRepositoryRegistry->subgraphForNode($element); - return ( - $subgraph->findReferences( - $element->nodeAggregateId, - FindReferencesFilter::create(referenceName: $propertyName) - )[0] ?? null - )?->node; - } - if ($this->getNodeType($element)->hasReference($propertyName)) { + // legacy access layer for references $subgraph = $this->contentRepositoryRegistry->subgraphForNode($element); - return $subgraph->findReferences( + $references = $subgraph->findReferences( $element->nodeAggregateId, FindReferencesFilter::create(referenceName: $propertyName) )->getNodes(); + + if (($this->getNodeType($element)->getReferences()[$propertyName]['constraints']['maxItems'] ?? -1) === 1) { + // legacy layer references with only one item like the previous `type: reference` + // (the node type transforms that to constraints.maxItems = 1) + // users still expect the property operation to return a single node instead of an array. + return $references->first(); + } + + return $references; } return $element->getProperty($propertyName); From 813f020b27048165d27199a07305941667fe9956 Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Wed, 3 Apr 2024 21:50:41 +0200 Subject: [PATCH 19/23] TASK: Adjust to `requireNodeType` --- .../Classes/Feature/Common/ConstraintChecks.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Neos.ContentRepository.Core/Classes/Feature/Common/ConstraintChecks.php b/Neos.ContentRepository.Core/Classes/Feature/Common/ConstraintChecks.php index 6af71b92d08..4502275c49e 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/Common/ConstraintChecks.php +++ b/Neos.ContentRepository.Core/Classes/Feature/Common/ConstraintChecks.php @@ -234,7 +234,7 @@ protected function requireNodeTypeToAllowNodesOfTypeInReference( protected function requireNodeTypeToAllowCountOfReferencesInReference(SerializedNodeReferences $nodeReferences, ReferenceName $referenceName, NodeTypeName $nodeTypeName): void { - $nodeType = $this->nodeTypeManager->getNodeType($nodeTypeName->value); + $nodeType = $this->requireNodeType($nodeTypeName); $maxItems = $nodeType->getReferences()[$referenceName->value]['constraints']['maxItems'] ?? null; if ($maxItems === null) { From 2760a05f6336e104b07aa5c8636c08c1392892c5 Mon Sep 17 00:00:00 2001 From: Bernhard Schmitt Date: Thu, 4 Apr 2024 00:01:08 +0200 Subject: [PATCH 20/23] Rename CountOf to NumberOf --- .../Classes/Feature/Common/ConstraintChecks.php | 2 +- .../Classes/Feature/NodeReferencing/NodeReferencing.php | 2 +- .../Classes/SharedModel/Exception/ReferenceCannotBeSet.php | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Neos.ContentRepository.Core/Classes/Feature/Common/ConstraintChecks.php b/Neos.ContentRepository.Core/Classes/Feature/Common/ConstraintChecks.php index 4502275c49e..b4e665910d4 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/Common/ConstraintChecks.php +++ b/Neos.ContentRepository.Core/Classes/Feature/Common/ConstraintChecks.php @@ -232,7 +232,7 @@ protected function requireNodeTypeToAllowNodesOfTypeInReference( } } - protected function requireNodeTypeToAllowCountOfReferencesInReference(SerializedNodeReferences $nodeReferences, ReferenceName $referenceName, NodeTypeName $nodeTypeName): void + protected function requireNodeTypeToAllowNumberOfReferencesInReference(SerializedNodeReferences $nodeReferences, ReferenceName $referenceName, NodeTypeName $nodeTypeName): void { $nodeType = $this->requireNodeType($nodeTypeName); diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeReferencing/NodeReferencing.php b/Neos.ContentRepository.Core/Classes/Feature/NodeReferencing/NodeReferencing.php index 156de9f692f..507e5c6bb6c 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/NodeReferencing/NodeReferencing.php +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeReferencing/NodeReferencing.php @@ -115,7 +115,7 @@ private function handleSetSerializedNodeReferences( ); $this->requireNodeTypeToDeclareReference($sourceNodeAggregate->nodeTypeName, $command->referenceName); - $this->requireNodeTypeToAllowCountOfReferencesInReference( + $this->requireNodeTypeToAllowNumberOfReferencesInReference( $command->references, $command->referenceName, $sourceNodeAggregate->nodeTypeName diff --git a/Neos.ContentRepository.Core/Classes/SharedModel/Exception/ReferenceCannotBeSet.php b/Neos.ContentRepository.Core/Classes/SharedModel/Exception/ReferenceCannotBeSet.php index eb178e40b29..46c6ca54780 100644 --- a/Neos.ContentRepository.Core/Classes/SharedModel/Exception/ReferenceCannotBeSet.php +++ b/Neos.ContentRepository.Core/Classes/SharedModel/Exception/ReferenceCannotBeSet.php @@ -51,11 +51,11 @@ public static function becauseTheNodeTypeConstraintsAreNotMatched( public static function becauseTheItemsCountConstraintsAreNotMatched( ReferenceName $referenceName, NodeTypeName $nodeTypeName, - int $countOfAttemptedReferencesToWrite + int $numberOfAttemptedReferencesToWrite ): self { return new self( 'Reference "' . $referenceName->value . '" cannot be set for node type "' - . $nodeTypeName->value . '" because the constraints do not allow to set ' . $countOfAttemptedReferencesToWrite . ' references', + . $nodeTypeName->value . '" because the constraints do not allow to set ' . $numberOfAttemptedReferencesToWrite . ' references', 1700150156 ); } From 544c48966f8a9bf97c5025b75ff4e4be479011ba Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Sat, 6 Apr 2024 11:09:11 +0200 Subject: [PATCH 21/23] TASK: Make default `reference(s)` editor configurable again --- .../DefaultPropertyEditorPostprocessor.php | 4 +++- Neos.Neos/Configuration/Settings.yaml | 6 ++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/Neos.Neos/Classes/NodeTypePostprocessor/DefaultPropertyEditorPostprocessor.php b/Neos.Neos/Classes/NodeTypePostprocessor/DefaultPropertyEditorPostprocessor.php index 0f9dd2b2bfa..45fc93ca39f 100644 --- a/Neos.Neos/Classes/NodeTypePostprocessor/DefaultPropertyEditorPostprocessor.php +++ b/Neos.Neos/Classes/NodeTypePostprocessor/DefaultPropertyEditorPostprocessor.php @@ -57,7 +57,9 @@ public function process(NodeType $nodeType, array &$configuration, array $option if (!$editor) { $maxAllowedItems = $referenceConfiguration['constraints']['maxItems'] ?? null; - $editor = $maxAllowedItems === 1 ? 'Neos.Neos/Inspector/Editors/ReferenceEditor' : 'Neos.Neos/Inspector/Editors/ReferencesEditor'; + $editor = $maxAllowedItems === 1 + ? ($this->dataTypesDefaultConfiguration['reference']['editor'] ?? 'Neos.Neos/Inspector/Editors/ReferenceEditor') + : ($this->dataTypesDefaultConfiguration['references']['editor'] ?? 'Neos.Neos/Inspector/Editors/ReferencesEditor'); } $mergedInspectorConfiguration = $this->editorDefaultConfiguration[$editor] ?? []; diff --git a/Neos.Neos/Configuration/Settings.yaml b/Neos.Neos/Configuration/Settings.yaml index 80fda86ef6c..11ca11829ba 100755 --- a/Neos.Neos/Configuration/Settings.yaml +++ b/Neos.Neos/Configuration/Settings.yaml @@ -230,6 +230,12 @@ Neos: editor: Neos.Neos/Inspector/Editors/DateTimeEditor editorOptions: format: d-m-Y + # special types uses for NodeType references to wire the "editor" + # singular "reference" will be used if constraints.maxItems is set to 1 + reference: + editor: Neos.Neos/Inspector/Editors/ReferenceEditor + references: + editor: Neos.Neos/Inspector/Editors/ReferencesEditor editors: Neos.Neos/Inspector/Editors/CodeEditor: editorOptions: From 6107c2d0e09891d1be9dcd900a159cc26d69e741 Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Sat, 6 Apr 2024 19:36:15 +0200 Subject: [PATCH 22/23] TASK: Add references test for DefaultPropertyEditor --- ...DefaultPropertyEditorPostprocessorTest.php | 91 ++++++++++++++++--- 1 file changed, 79 insertions(+), 12 deletions(-) diff --git a/Neos.Neos/Tests/Unit/NodeTypePostprocessor/DefaultPropertyEditorPostprocessorTest.php b/Neos.Neos/Tests/Unit/NodeTypePostprocessor/DefaultPropertyEditorPostprocessorTest.php index 59b24e1c5db..dd2a83b674a 100644 --- a/Neos.Neos/Tests/Unit/NodeTypePostprocessor/DefaultPropertyEditorPostprocessorTest.php +++ b/Neos.Neos/Tests/Unit/NodeTypePostprocessor/DefaultPropertyEditorPostprocessorTest.php @@ -17,25 +17,77 @@ use Neos\ContentRepository\Core\NodeType\NodeTypeName; use Neos\Flow\Tests\UnitTestCase; use Neos\Neos\NodeTypePostprocessor\DefaultPropertyEditorPostprocessor; +use Symfony\Component\Yaml\Yaml; /** * Testcase for the DefaultPropertyEditorPostprocessor */ class DefaultPropertyEditorPostprocessorTest extends UnitTestCase { - private function processConfiguration(array $configuration, array $dataTypesDefaultConfiguration, array $editorDefaultConfiguration): array + public function referenceExamples(): iterable { - $postprocessor = new DefaultPropertyEditorPostprocessor(); - $this->inject($postprocessor, 'dataTypesDefaultConfiguration', $dataTypesDefaultConfiguration); - $this->inject($postprocessor, 'editorDefaultConfiguration', $editorDefaultConfiguration); - $mockNodeType = new NodeType( - NodeTypeName::fromString('Some.NodeType:Name'), - [], - [], - new DefaultNodeLabelGeneratorFactory() - ); - $postprocessor->process($mockNodeType, $configuration, []); - return $configuration; + yield 'multiple references' => [ + 'nodeTypeDefinition' => <<<'YAML' + references: + someReferences: + ui: + inspector: + group: 'foo' + YAML, + 'expected' => <<<'YAML' + references: + someReferences: + ui: + inspector: + group: 'foo' + editor: ReferencesEditor + YAML + ]; + + yield 'singular reference' => [ + 'nodeTypeDefinition' => <<<'YAML' + references: + someReference: + constraints: + maxItems: 1 + ui: + inspector: + group: 'foo' + YAML, + 'expected' => <<<'YAML' + references: + someReference: + constraints: + maxItems: 1 + ui: + inspector: + editor: SingularReferenceEditor + group: 'foo' + YAML + ]; + } + + /** + * @test + * @dataProvider referenceExamples + */ + public function processExamples(string $nodeTypeDefinition, string $expectedResult) + { + $configuration = array_merge(['references' => [], 'properties' => []], Yaml::parse($nodeTypeDefinition)); + + $dataTypesDefaultConfiguration = [ + 'reference' => [ + 'editor' => 'SingularReferenceEditor', + ], + 'references' => [ + 'editor' => 'ReferencesEditor', + ], + ]; + + $editorDefaultConfiguration = []; + + $actualResult = $this->processConfiguration($configuration, $dataTypesDefaultConfiguration, $editorDefaultConfiguration); + self::assertEquals(array_merge(['references' => [], 'properties' => []], Yaml::parse($expectedResult)), $actualResult); } /** @@ -283,4 +335,19 @@ public function processThrowsExceptionIfNoPropertyEditorCanBeResolved(): void ]; $this->processConfiguration($configuration, $dataTypesDefaultConfiguration, []); } + + private function processConfiguration(array $configuration, array $dataTypesDefaultConfiguration, array $editorDefaultConfiguration): array + { + $postprocessor = new DefaultPropertyEditorPostprocessor(); + $this->inject($postprocessor, 'dataTypesDefaultConfiguration', $dataTypesDefaultConfiguration); + $this->inject($postprocessor, 'editorDefaultConfiguration', $editorDefaultConfiguration); + $mockNodeType = new NodeType( + NodeTypeName::fromString('Some.NodeType:Name'), + [], + [], + new DefaultNodeLabelGeneratorFactory() + ); + $postprocessor->process($mockNodeType, $configuration, []); + return $configuration; + } } From e282c1497567791eb3e602d0191cad7d52914f68 Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Sat, 6 Apr 2024 21:32:22 +0200 Subject: [PATCH 23/23] TASK: Simplify expression --- .../Classes/FlowQueryOperations/PropertyOperation.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Neos.ContentRepository.NodeAccess/Classes/FlowQueryOperations/PropertyOperation.php b/Neos.ContentRepository.NodeAccess/Classes/FlowQueryOperations/PropertyOperation.php index 9c797d0436b..fe612a2cd5f 100644 --- a/Neos.ContentRepository.NodeAccess/Classes/FlowQueryOperations/PropertyOperation.php +++ b/Neos.ContentRepository.NodeAccess/Classes/FlowQueryOperations/PropertyOperation.php @@ -121,7 +121,8 @@ public function evaluate(FlowQuery $flowQuery, array $arguments): mixed FindReferencesFilter::create(referenceName: $propertyName) )->getNodes(); - if (($this->getNodeType($element)->getReferences()[$propertyName]['constraints']['maxItems'] ?? -1) === 1) { + $maxItems = $this->getNodeType($element)->getReferences()[$propertyName]['constraints']['maxItems'] ?? null; + if ($maxItems === 1) { // legacy layer references with only one item like the previous `type: reference` // (the node type transforms that to constraints.maxItems = 1) // users still expect the property operation to return a single node instead of an array.