Skip to content

Commit

Permalink
Merge pull request #62 from Flowpack/bugfix/61-triggerPropertyMapping
Browse files Browse the repository at this point in the history
BUGFIX: Use property mapping for `type: SomeClass` or `array<SomeClass>`
  • Loading branch information
Sebobo authored Jun 19, 2023
2 parents 2c69d58 + 847e738 commit 970643b
Show file tree
Hide file tree
Showing 14 changed files with 276 additions and 10 deletions.
50 changes: 48 additions & 2 deletions Classes/Domain/NodeCreation/NodeCreationService.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,15 @@
use Flowpack\NodeTemplates\Domain\Template\Template;
use Flowpack\NodeTemplates\Domain\Template\Templates;
use Neos\ContentRepository\Domain\Model\NodeInterface;
use Neos\ContentRepository\Domain\Model\NodeType;
use Neos\ContentRepository\Domain\Service\NodeTypeManager;
use Neos\ContentRepository\Exception\NodeConstraintException;
use Neos\Flow\Annotations as Flow;
use Neos\Flow\Property\PropertyMapper;
use Neos\Flow\Property\PropertyMappingConfiguration;
use Neos\Neos\Service\NodeOperations;
use Neos\Neos\Utility\NodeUriPathSegmentGenerator;
use Neos\Flow\Property\Exception as PropertyWasNotMappedException;

class NodeCreationService
{
Expand All @@ -34,14 +38,20 @@ class NodeCreationService
*/
protected $nodeUriPathSegmentGenerator;

/**
* @Flow\Inject
* @var PropertyMapper
*/
protected $propertyMapper;

/**
* Applies the root template and its descending configured child node templates on the given node.
* @throws \InvalidArgumentException
*/
public function apply(RootTemplate $template, NodeInterface $node, CaughtExceptions $caughtExceptions): void
{
$nodeType = $node->getNodeType();
$propertiesAndReferences = PropertiesAndReferences::createFromArrayAndTypeDeclarations($template->getProperties(), $nodeType);
$propertiesAndReferences = PropertiesAndReferences::createFromArrayAndTypeDeclarations($this->convertProperties($nodeType, $template->getProperties(), $caughtExceptions), $nodeType);

// set properties
foreach ($propertiesAndReferences->requireValidProperties($nodeType, $caughtExceptions) as $key => $value) {
Expand Down Expand Up @@ -119,7 +129,7 @@ private function applyTemplateRecursively(Templates $templates, NodeInterface $p
}
}
$nodeType = $node->getNodeType();
$propertiesAndReferences = PropertiesAndReferences::createFromArrayAndTypeDeclarations($template->getProperties(), $nodeType);
$propertiesAndReferences = PropertiesAndReferences::createFromArrayAndTypeDeclarations($this->convertProperties($nodeType, $template->getProperties(), $caughtExceptions), $nodeType);

// set properties
foreach ($propertiesAndReferences->requireValidProperties($nodeType, $caughtExceptions) as $key => $value) {
Expand Down Expand Up @@ -153,4 +163,40 @@ private function ensureNodeHasUriPathSegment(NodeInterface $node, $template)
}
$node->setProperty('uriPathSegment', $this->nodeUriPathSegmentGenerator->generateUriPathSegment($node, $properties['title'] ?? null));
}

private function convertProperties(NodeType $nodeType, array $properties, CaughtExceptions $caughtExceptions): array
{
// TODO combine with PropertiesAndReferences::requireValidProperties
foreach ($nodeType->getConfiguration('properties') as $propertyName => $propertyConfiguration) {
if (!isset($properties[$propertyName])) {
continue;
}
$propertyType = $nodeType->getPropertyType($propertyName);
if ($propertyType === 'references' || $propertyType === 'reference') {
continue;
}
$propertyType = PropertyType::fromPropertyOfNodeType($propertyName, $nodeType);
$propertyValue = $properties[$propertyName];
if (!$propertyType->isClass() && !$propertyType->isArrayOfClass()) {
// property mapping only for class types or array of classes!
continue;
}
try {
$propertyMappingConfiguration = new PropertyMappingConfiguration();
$propertyMappingConfiguration->allowAllProperties();

$properties[$propertyName] = $this->propertyMapper->convert($propertyValue, $propertyType->getValue(), $propertyMappingConfiguration);
$messages = $this->propertyMapper->getMessages();
if ($messages->hasErrors()) {
throw new PropertyWasNotMappedException($this->propertyMapper->getMessages()->getFirstError()->getMessage(), 1686779371122);
}
} catch (PropertyWasNotMappedException $exception) {
$caughtExceptions->add(CaughtException::fromException(
$exception
)->withOrigin(sprintf('Property "%s" in NodeType "%s"', $propertyName, $nodeType->getName())));
unset($properties[$propertyName]);
}
}
return $properties;
}
}
13 changes: 13 additions & 0 deletions Classes/Domain/NodeCreation/PropertyType.php
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,19 @@ public function isArrayOf(): bool
return (bool)preg_match(self::PATTERN_ARRAY_OF, $this->value);
}

public function isArrayOfClass(): bool
{
return $this->isArrayOf() && $this->arrayOfType->isClass();
}

public function isClass(): bool
{
$className = $this->value[0] != '\\'
? '\\' . $this->value
: $this->value;
return (class_exists($className) || interface_exists($className));
}

public function isDate(): bool
{
return $this->value === self::TYPE_DATE;
Expand Down
23 changes: 23 additions & 0 deletions Configuration/Testing/NodeTypes.ResolvablePropertyValues.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Test, that asset ids are correctly resolved to asset objects (via the property mapper)
# Also reference node id's should be correctly resolved
---

'Flowpack.NodeTemplates:Content.ResolvablePropertyValues':
superTypes:
'Neos.Neos:Content': true
properties:
asset:
type: Neos\Media\Domain\Model\Asset
images:
type: array<Neos\Media\Domain\Model\ImageInterface>
reference:
type: reference
references:
type: references
options:
template:
properties:
asset: 'c228200e-7472-4290-9936-4454a5b5692a'
reference: 'some-node-id'
references: "${['some-node-id', 'other-node-id', data.realNode]}"
images: "${['c8ae9f9f-dd11-4373-bf42-4bf31ec5bd19']}"
27 changes: 27 additions & 0 deletions Configuration/Testing/NodeTypes.UnresolvablePropertyValues.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# We make sure that we dont trigger unwanted property mapping, so we wont allow an array in a string field.
---

'Flowpack.NodeTemplates:Content.UnresolvablePropertyValues':
superTypes:
'Neos.Neos:Content': true
ui:
label: UnresolvablePropertyValues
properties:
someString:
type: string
asset:
type: Neos\Media\Domain\Model\Asset
images:
type: array<Neos\Media\Domain\Model\ImageInterface>
reference:
type: reference
references:
type: references
options:
template:
properties:
someString: "${['foo']}"
reference: true
references: "${['some-non-existing-node-id']}"
asset: "non-existing"
images: "${['non-existing']}"
26 changes: 26 additions & 0 deletions Tests/Functional/Fixtures/ResolvablePropertyValues.nodes.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"properties": {
"asset": "object(Neos\\Media\\Domain\\Model\\Asset, c228200e-7472-4290-9936-4454a5b5692a)",
"images": [
"object(Neos\\Media\\Domain\\Model\\Image, c8ae9f9f-dd11-4373-bf42-4bf31ec5bd19)"
]
},
"references": {
"reference": [
{
"node": "Node(some-node-id, unstructured)"
}
],
"references": [
{
"node": "Node(some-node-id, unstructured)"
},
{
"node": "Node(other-node-id, unstructured)"
},
{
"node": "Node(real-node-id, unstructured)"
}
]
}
}
15 changes: 15 additions & 0 deletions Tests/Functional/Fixtures/ResolvablePropertyValues.template.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"properties": {
"asset": "c228200e-7472-4290-9936-4454a5b5692a",
"reference": "some-node-id",
"references": [
"some-node-id",
"other-node-id",
"Node(real-node-id, unstructured)"
],
"images": [
"c8ae9f9f-dd11-4373-bf42-4bf31ec5bd19"
]
},
"childNodes": []
}
8 changes: 8 additions & 0 deletions Tests/Functional/Fixtures/ResolvablePropertyValues.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
'{nodeTypeName}':
options:
template:
properties:
# asset -> object(Neos\Media\Domain\Model\Asset)
# images -> array(Neos\Media\Domain\Model\Image)
# reference -> Reference of NodeTypes (Neos.Neos:Document) with value Node(some-node-id)
# references -> Nodes(some-node-id, other-node-id, real-node-id)
26 changes: 26 additions & 0 deletions Tests/Functional/Fixtures/UnresolvablePropertyValues.messages.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[
{
"message": "Template for \"UnresolvablePropertyValues\" only partially applied. Please check the newly created nodes beneath Node \/sites\/test-site\/homepage\/main\/new-node@live[Flowpack.NodeTemplates:Content.UnresolvablePropertyValues].",
"severity": "ERROR"
},
{
"message": "Property \"asset\" in NodeType \"Flowpack.NodeTemplates:Content.UnresolvablePropertyValues\" | FlowException(Object of type \"Neos\\Media\\Domain\\Model\\Asset\" with identity \"non-existing\" not found., 1686779371122)",
"severity": "ERROR"
},
{
"message": "Property \"images\" in NodeType \"Flowpack.NodeTemplates:Content.UnresolvablePropertyValues\" | FlowException(Could not convert target type \"array<Neos\\Media\\Domain\\Model\\ImageInterface>\", at property path \"0\": No converter found which can be used to convert from \"string\" to \"Neos\\Media\\Domain\\Model\\ImageInterface\"., 1297759968) | TypeConverterException(No converter found which can be used to convert from \"string\" to \"Neos\\Media\\Domain\\Model\\ImageInterface\"., 0)",
"severity": "ERROR"
},
{
"message": "Property \"someString\" in NodeType \"Flowpack.NodeTemplates:Content.UnresolvablePropertyValues\" | PropertyIgnoredException(Because value `[\"foo\"]` is not assignable to property type \"string\"., 1685958105644)",
"severity": "ERROR"
},
{
"message": "Reference \"reference\" in NodeType \"Flowpack.NodeTemplates:Content.UnresolvablePropertyValues\" | RuntimeException(Reference could not be set, because node reference(s) true cannot be resolved., 1685958176560)",
"severity": "ERROR"
},
{
"message": "Reference \"references\" in NodeType \"Flowpack.NodeTemplates:Content.UnresolvablePropertyValues\" | RuntimeException(Reference could not be set, because node reference(s) [\"some-non-existing-node-id\"] cannot be resolved., 1685958176560)",
"severity": "ERROR"
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[]
16 changes: 16 additions & 0 deletions Tests/Functional/Fixtures/UnresolvablePropertyValues.template.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"properties": {
"someString": [
"foo"
],
"reference": true,
"references": [
"some-non-existing-node-id"
],
"asset": "non-existing",
"images": [
"non-existing"
]
},
"childNodes": []
}
3 changes: 3 additions & 0 deletions Tests/Functional/Fixtures/UnresolvablePropertyValues.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
'{nodeTypeName}':
options:
template: []
4 changes: 3 additions & 1 deletion Tests/Functional/JsonSerializeNodeTreeTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Flowpack\NodeTemplates\Tests\Functional;

use Neos\ContentRepository\Domain\Model\NodeInterface;
use Neos\Utility\ObjectAccess;

trait JsonSerializeNodeTreeTrait
{
Expand Down Expand Up @@ -51,7 +52,8 @@ private function serializeValuesInArray(array $array): array
$value = $this->serializeValuesInArray($value);
}
} elseif (is_object($value)) {
$value = sprintf('object(%s)', get_class($value));
$id = ObjectAccess::getProperty($value, 'Persistence_Object_Identifier', true);
$value = sprintf('object(%s%s)', get_class($value), $id ? (sprintf(', %s', $id)) : '');
} else {
continue;
}
Expand Down
Loading

0 comments on commit 970643b

Please sign in to comment.