Skip to content

Commit

Permalink
Merge pull request #5046 from mhsdesign/feature/nodeAccessFlowQueryOp…
Browse files Browse the repository at this point in the history
…erations

FEATURE: 9.0 Node access flow query operations
  • Loading branch information
mhsdesign committed May 13, 2024
2 parents ee035e7 + 5ba50e9 commit 162680f
Show file tree
Hide file tree
Showing 8 changed files with 297 additions and 7 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?php
namespace Neos\ContentRepository\NodeAccess\FlowQueryOperations;

/*
* This file is part of the Neos.ContentRepository package.
*
* (c) Contributors of the Neos Project - www.neos.io
*
* This package is Open Source Software. For the full copyright and license
* information, please view the LICENSE file which was distributed with this
* source code.
*/

use Neos\ContentRepository\Core\Projection\ContentGraph\Node;
use Neos\Eel\FlowQuery\FlowQuery;
use Neos\Eel\FlowQuery\FlowQueryException;
use Neos\Eel\FlowQuery\Operations\AbstractOperation;

/**
* Used to access the Node's identifier of a ContentRepository Node.
*/
class IdOperation extends AbstractOperation
{
/**
* {@inheritdoc}
*
* @var string
*/
protected static $shortName = 'id';

/**
* {@inheritdoc}
*
* @var integer
*/
protected static $priority = 100;

/**
* {@inheritdoc}
*
* @var boolean
*/
protected static $final = true;

/**
* {@inheritdoc}
*
* We can only handle ContentRepository Nodes.
*
* @param array<int, mixed> $context $context onto which this operation should be applied (array or array-like object)
* @return boolean
*/
public function canEvaluate($context): bool
{
return (isset($context[0]) && $context[0] instanceof Node);
}

/**
* {@inheritdoc}
*
* @param FlowQuery<int,mixed> $flowQuery the FlowQuery object
* @param array<int,mixed> $arguments the arguments for this operation
* @return mixed
* @throws FlowQueryException
*/
public function evaluate(FlowQuery $flowQuery, array $arguments)
{
if ($arguments !== []) {
throw new FlowQueryException(static::$shortName . '() does not require any argument.', 1715510778);
}
/** @var array<int,mixed> $context */
$context = $flowQuery->getContext();
$node = $context[0] ?? null;
if (!$node instanceof Node) {
return null;
}
return $node->nodeAggregateId->value;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?php
namespace Neos\ContentRepository\NodeAccess\FlowQueryOperations;

/*
* This file is part of the Neos.ContentRepository package.
*
* (c) Contributors of the Neos Project - www.neos.io
*
* This package is Open Source Software. For the full copyright and license
* information, please view the LICENSE file which was distributed with this
* source code.
*/

use Neos\ContentRepository\Core\Projection\ContentGraph\Node;
use Neos\Eel\FlowQuery\FlowQuery;
use Neos\Eel\FlowQuery\FlowQueryException;
use Neos\Eel\FlowQuery\Operations\AbstractOperation;

/**
* Used to access the Node's label of a ContentRepository Node.
*/
class LabelOperation extends AbstractOperation
{
/**
* {@inheritdoc}
*
* @var string
*/
protected static $shortName = 'label';

/**
* {@inheritdoc}
*
* @var integer
*/
protected static $priority = 100;

/**
* {@inheritdoc}
*
* @var boolean
*/
protected static $final = true;

/**
* {@inheritdoc}
*
* We can only handle ContentRepository Nodes.
*
* @param array<int, mixed> $context $context onto which this operation should be applied (array or array-like object)
* @return boolean
*/
public function canEvaluate($context): bool
{
return (isset($context[0]) && $context[0] instanceof Node);
}

/**
* {@inheritdoc}
*
* @param FlowQuery<int,mixed> $flowQuery the FlowQuery object
* @param array<int,mixed> $arguments the arguments for this operation
* @return mixed
* @throws FlowQueryException
*/
public function evaluate(FlowQuery $flowQuery, array $arguments)
{
if ($arguments !== []) {
throw new FlowQueryException(static::$shortName . '() does not require any argument.', 1715510778);
}
/** @var array<int,mixed> $context */
$context = $flowQuery->getContext();
$node = $context[0] ?? null;
if (!$node instanceof Node) {
return null;
}
return $node->getLabel();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?php
namespace Neos\ContentRepository\NodeAccess\FlowQueryOperations;

/*
* This file is part of the Neos.ContentRepository package.
*
* (c) Contributors of the Neos Project - www.neos.io
*
* This package is Open Source Software. For the full copyright and license
* information, please view the LICENSE file which was distributed with this
* source code.
*/

use Neos\ContentRepository\Core\Projection\ContentGraph\Node;
use Neos\Eel\FlowQuery\FlowQuery;
use Neos\Eel\FlowQuery\FlowQueryException;
use Neos\Eel\FlowQuery\Operations\AbstractOperation;

/**
* Used to access the NodeTypeName of a ContentRepository Node.
*/
class NodeTypeNameOperation extends AbstractOperation
{
/**
* {@inheritdoc}
*
* @var string
*/
protected static $shortName = 'nodeTypeName';

/**
* {@inheritdoc}
*
* @var integer
*/
protected static $priority = 100;

/**
* {@inheritdoc}
*
* @var boolean
*/
protected static $final = true;

/**
* {@inheritdoc}
*
* We can only handle ContentRepository Nodes.
*
* @param array<int, mixed> $context $context onto which this operation should be applied (array or array-like object)
* @return boolean
*/
public function canEvaluate($context): bool
{
return (isset($context[0]) && $context[0] instanceof Node);
}

/**
* {@inheritdoc}
*
* @param FlowQuery<int,mixed> $flowQuery the FlowQuery object
* @param array<int,mixed> $arguments the arguments for this operation
* @return mixed
* @throws FlowQueryException
*/
public function evaluate(FlowQuery $flowQuery, array $arguments)
{
if ($arguments !== []) {
throw new FlowQueryException(static::$shortName . '() does not require any argument.', 1715510778);
}
/** @var array<int,mixed> $context */
$context = $flowQuery->getContext();
$node = $context[0] ?? null;
if (!$node instanceof Node) {
return null;
}
return $node->nodeTypeName->value;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,6 @@ class PropertyOperation extends AbstractOperation
*/
protected $contentRepositoryRegistry;

use NodeTypeWithFallbackProvider;

/**
* {@inheritdoc}
*
Expand Down Expand Up @@ -113,15 +111,18 @@ public function evaluate(FlowQuery $flowQuery, array $arguments): mixed
return ObjectAccess::getPropertyPath($element, substr($propertyName, 1));
}

if ($this->getNodeType($element)->hasReference($propertyName)) {
$contentRepository = $this->contentRepositoryRegistry->get($element->subgraphIdentity->contentRepositoryId);
$nodeTypeManager = $contentRepository->getNodeTypeManager();

if ($nodeTypeManager->getNodeType($element->nodeTypeName)?->hasReference($propertyName)) {
// legacy access layer for references
$subgraph = $this->contentRepositoryRegistry->subgraphForNode($element);
$references = $subgraph->findReferences(
$element->nodeAggregateId,
FindReferencesFilter::create(referenceName: $propertyName)
)->getNodes();

$maxItems = $this->getNodeType($element)->getReferences()[$propertyName]['constraints']['maxItems'] ?? null;
$maxItems = $nodeTypeManager->getNodeType($element->nodeTypeName)->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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ class ExpressionBasedNodeLabelGenerator implements NodeLabelGeneratorInterface
/**
* @var string
*/
protected $expression = '${(node.nodeType.label ? node.nodeType.label : node.nodeType.name) + \' (\' + node.name + \')\'}';
protected $expression = <<<'EEL'
${(node.nodeType.label ? node.nodeType.label : node.nodeType.name) + (node.nodeName ? ' (' + node.nodeName.value + ')' : '')}
EEL;

/**
* @return string
Expand Down
6 changes: 6 additions & 0 deletions Neos.Neos/Classes/Fusion/Helper/NodeHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,12 @@ public function getNodeType(Node $node): NodeType
return $this->getNodeTypeInternal($node);
}

public function isNodeTypeExistent(Node $node): bool
{
$contentRepository = $this->contentRepositoryRegistry->get($node->subgraphIdentity->contentRepositoryId);
return $contentRepository->getNodeTypeManager()->hasNodeType($node->nodeTypeName);
}

public function serializedNodeAddress(Node $node): string
{
$contentRepository = $this->contentRepositoryRegistry->get(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ prototype(Neos.Neos:ContentCase) < prototype(Neos.Fusion:Case) {
default {
@position = 'end'
condition = true
# this eel helper also handles the Neos.Neos:FallbackNode node type mechanism
type = ${Neos.Node.getNodeType(node).name.value}
type = ${Neos.Node.isNodeTypeExistent(node) ? q(node).nodeTypeName() : 'Neos.Neos:FallbackNode'}
}
}
45 changes: 45 additions & 0 deletions Neos.Neos/Tests/Behavior/Features/Fusion/FlowQuery.feature
Original file line number Diff line number Diff line change
Expand Up @@ -395,3 +395,48 @@ Feature: Tests for the "Neos.ContentRepository" Flow Query methods.
removeNode: a
nothingToRemove: a1a4,a1a4,a1a4
"""

Scenario: Node accessors (final Node access operations)
When the Fusion context node is "a1"
When I execute the following Fusion code:
"""fusion
test = Neos.Fusion:DataStructure {
property = ${q(node).property('title')}
identifier = ${q(node).id()}
label = ${q(node).label()}
nodeTypeName = ${q(node).nodeTypeName()}
@process.render = ${Json.stringify(value, ['JSON_PRETTY_PRINT'])}
}
"""
Then I expect the following Fusion rendering result:
"""
{
"property": "Node a1",
"identifier": "a1",
"label": "Neos.Neos:Test.DocumentType1",
"nodeTypeName": "Neos.Neos:Test.DocumentType1"
}
"""
# if the node type config is empty, the operation should still work
When I change the node types in content repository "default" to:
"""yaml
"""
When I execute the following Fusion code:
"""fusion
test = Neos.Fusion:DataStructure {
property = ${q(node).property('title')}
identifier = ${q(node).id()}
label = ${q(node).label()}
nodeTypeName = ${q(node).nodeTypeName()}
@process.render = ${Json.stringify(value, ['JSON_PRETTY_PRINT'])}
}
"""
Then I expect the following Fusion rendering result:
"""
{
"property": "Node a1",
"identifier": "a1",
"label": "Neos.Neos:Test.DocumentType1",
"nodeTypeName": "Neos.Neos:Test.DocumentType1"
}
"""

0 comments on commit 162680f

Please sign in to comment.