forked from neos/neos-development-collection
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
FEATURE: Add
findByCriteria
and findByIdentifier
flowQuery operation
`findByCriteria` allows to query the subgraph below the contextNode with the following arguments: - string | null: nodeTypefilter - string | null: propertyValueCriteria - object{offset?:int, limit?:int} | null: pagination `findByIdentifier` will find a node with the given aggregate id in the subgraph defined by the contextNode: - string: nodeAggregateId Resolves: neos#5434
- Loading branch information
Showing
3 changed files
with
364 additions
and
0 deletions.
There are no files selected for viewing
207 changes: 207 additions & 0 deletions
207
Neos.ContentRepository.NodeAccess/Classes/FlowQueryOperations/FindByCriteriaOperation.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,207 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
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\NodeType\NodeTypeName; | ||
use Neos\ContentRepository\Core\NodeType\NodeTypeNames; | ||
use Neos\ContentRepository\Core\Projection\ContentGraph\AbsoluteNodePath; | ||
use Neos\ContentRepository\Core\Projection\ContentGraph\ContentSubgraphInterface; | ||
use Neos\ContentRepository\Core\Projection\ContentGraph\Filter\FindChildNodesFilter; | ||
use Neos\ContentRepository\Core\Projection\ContentGraph\Filter\FindDescendantNodesFilter; | ||
use Neos\ContentRepository\Core\Projection\ContentGraph\Filter\NodeType\NodeTypeCriteria; | ||
use Neos\ContentRepository\Core\Projection\ContentGraph\Filter\Pagination\Pagination; | ||
use Neos\ContentRepository\Core\Projection\ContentGraph\Filter\PropertyValue\PropertyValueCriteriaParser; | ||
use Neos\ContentRepository\Core\Projection\ContentGraph\Filter\SearchTerm\SearchTerm; | ||
use Neos\ContentRepository\Core\Projection\ContentGraph\Node; | ||
use Neos\ContentRepository\Core\Projection\ContentGraph\NodePath; | ||
use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId; | ||
use Neos\ContentRepositoryRegistry\ContentRepositoryRegistry; | ||
use Neos\Eel\FlowQuery\FizzleParser; | ||
use Neos\Eel\FlowQuery\FlowQuery; | ||
use Neos\Eel\FlowQuery\FlowQueryException; | ||
use Neos\Eel\FlowQuery\Operations\AbstractOperation; | ||
|
||
/** | ||
* "findByCriteria" operation working on ContentRepository nodes. This operation allows for retrieval of descendant nodes | ||
* | ||
* Argument 1 (string|null): nodeTypeFilter, A list of NodeType Names seperated by ",", disallowed NodeTypes are prefixed with "!" | ||
* | ||
* Argument 2 (string|null): propertyValueFilter | ||
* | ||
* The following comparisons are supported: | ||
* | ||
* property value equals the specified value (string, integer, float or boolean): | ||
* | ||
* "prop = 'some string'" | ||
* "prop = 123" | ||
* "prop = 123.45" | ||
* "prop = true" | ||
* | ||
* property value is NOT equal to the specified value (string, integer, float or boolean): | ||
* | ||
* "prop != 'some string'" | ||
* "prop != 123" | ||
* "prop != 123.45" | ||
* "prop != true" | ||
* | ||
* property value ends with the specified substring | ||
* | ||
* "prop $= 'some string'" | ||
* | ||
* property value starts with the specified substring | ||
* | ||
* "prop ^= 'some string'" | ||
* | ||
* property value contains the specified substring | ||
* | ||
* "prop *= 'some string'" | ||
* | ||
* property value is greater than the specified value (string, integer or float): | ||
* | ||
* "prop > 'some string'" | ||
* "prop > 123" | ||
* "prop > 123.45" | ||
* | ||
* property value is greater than or equal to the specified value (string, integer or float): | ||
* | ||
* "prop >= 'some string'" | ||
* "prop >= 123" | ||
* "prop >= 123.45" | ||
* | ||
* property value is less than the specified value (string, integer or float): | ||
* | ||
* "prop < 'some string'" | ||
* "prop < 123" | ||
* "prop < 123.45" | ||
* | ||
* property value is less than or equal to the specified value (string, integer or float): | ||
* | ||
* "prop <= 'some string'" | ||
* "prop <= 123" | ||
* "prop <= 123.45" | ||
* | ||
* criteria can be combined using "AND" and "OR": | ||
* | ||
* "prop1 ^= 'foo' AND (prop2 = 'bar' OR prop3 = 'baz')" | ||
* | ||
* furthermore "NOT" can be used to negate a whole sub query | ||
* | ||
* "prop1 ^= 'foo' AND NOT (prop2 = 'bar' OR prop3 = 'baz')" | ||
* | ||
* | ||
* Argument 3 ({limit?:int, offset?:int}}): pagination | ||
* | ||
* | ||
* Example (node type): | ||
* | ||
* q(node).findByCriteria('Neos.NodeTypes:Text') | ||
* | ||
* Example (multiple node types): | ||
* | ||
* q(node).findByCriteria('[instanceof Neos.NodeTypes:Text],[instanceof Neos.NodeTypes:Image]') | ||
* | ||
* Example (node type with property filter): | ||
* | ||
* q(node).findByCriteria('Neos.NodeTypes:Text', 'text*="Neos"') | ||
*/ | ||
class FindByCriteriaOperation extends AbstractOperation | ||
{ | ||
use CreateNodeHashTrait; | ||
|
||
/** | ||
* {@inheritdoc} | ||
* | ||
* @var string | ||
*/ | ||
protected static $shortName = 'findByCriteria'; | ||
|
||
/** | ||
* {@inheritdoc} | ||
* | ||
* @var integer | ||
*/ | ||
protected static $priority = 100; | ||
|
||
/** | ||
* @Flow\Inject | ||
* @var ContentRepositoryRegistry | ||
*/ | ||
protected $contentRepositoryRegistry; | ||
|
||
/** | ||
* {@inheritdoc} | ||
* | ||
* @param array<int,mixed> $context (or array-like object) onto which this operation should be applied | ||
* @return boolean true if the operation can be applied onto the $context, false otherwise | ||
*/ | ||
public function canEvaluate($context) | ||
{ | ||
foreach ($context as $contextNode) { | ||
if (!$contextNode instanceof Node) { | ||
return false; | ||
} | ||
} | ||
|
||
return true; | ||
} | ||
/** | ||
* This operation operates rather on the given Context object than on the given node | ||
* and thus may work with the legacy node interface until subgraphs are available | ||
* {@inheritdoc} | ||
* | ||
* @param FlowQuery<int,mixed> $flowQuery the FlowQuery object | ||
* @param array<int,mixed> $arguments the arguments for this operation | ||
* @throws FlowQueryException | ||
* @throws \Neos\Eel\Exception | ||
* @throws \Neos\Eel\FlowQuery\FizzleException | ||
*/ | ||
public function evaluate(FlowQuery $flowQuery, array $arguments): void | ||
{ | ||
/** @var array<int,Node> $contextNodes */ | ||
$contextNodes = $flowQuery->getContext(); | ||
if (count($contextNodes) === 0) { | ||
return; | ||
} | ||
|
||
$firstContextNode = reset($contextNodes); | ||
assert($firstContextNode instanceof Node); | ||
|
||
$nodeTypeFilter = $arguments[0] ?? null; | ||
$propertyValueFilter = $arguments[1] ?? null; | ||
$pagination = $arguments[2] ?? null; | ||
|
||
assert($nodeTypeFilter === null || is_string($nodeTypeFilter)); | ||
assert($propertyValueFilter === null || is_string($propertyValueFilter)); | ||
assert($pagination === null || is_array($pagination)); | ||
|
||
/** @var Node[] $result */ | ||
$result = []; | ||
$findDescendentNodesFilter = FindDescendantNodesFilter::create( | ||
nodeTypes: $nodeTypeFilter ? NodeTypeCriteria::fromFilterString($nodeTypeFilter) : null, | ||
propertyValue: $propertyValueFilter ? PropertyValueCriteriaParser::parse($propertyValueFilter) : null, | ||
pagination: $pagination ? Pagination::fromArray($pagination) : null | ||
); | ||
|
||
/** @var Node $contextNode */ | ||
foreach ($flowQuery->getContext() as $contextNode) { | ||
$subgraph = $this->contentRepositoryRegistry->subgraphForNode($contextNode); | ||
foreach ($subgraph->findDescendantNodes($contextNode->aggregateId, $findDescendentNodesFilter) as $descendant) { | ||
$result[] = $descendant; | ||
} | ||
} | ||
|
||
$flowQuery->setContext($result); | ||
} | ||
} |
114 changes: 114 additions & 0 deletions
114
Neos.ContentRepository.NodeAccess/Classes/FlowQueryOperations/FindByIdentifierOperation.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
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\Filter\FindDescendantNodesFilter; | ||
use Neos\ContentRepository\Core\Projection\ContentGraph\Filter\NodeType\NodeTypeCriteria; | ||
use Neos\ContentRepository\Core\Projection\ContentGraph\Filter\Pagination\Pagination; | ||
use Neos\ContentRepository\Core\Projection\ContentGraph\Filter\PropertyValue\PropertyValueCriteriaParser; | ||
use Neos\ContentRepository\Core\Projection\ContentGraph\Node; | ||
use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId; | ||
use Neos\ContentRepositoryRegistry\ContentRepositoryRegistry; | ||
use Neos\Eel\FlowQuery\FlowQuery; | ||
use Neos\Eel\FlowQuery\FlowQueryException; | ||
use Neos\Eel\FlowQuery\Operations\AbstractOperation; | ||
|
||
/** | ||
* "findByIdentifier" operation working on ContentRepository nodes. This operation allows for retrieval of nodes by identifier | ||
* from the current subgraph | ||
* | ||
* Example: | ||
* | ||
* q(site).findByIdentifier('30e893c1-caef-0ca5-b53d-e5699bb8e506') | ||
*/ | ||
class FindByIdentifierOperation extends AbstractOperation | ||
{ | ||
use CreateNodeHashTrait; | ||
|
||
/** | ||
* {@inheritdoc} | ||
* | ||
* @var string | ||
*/ | ||
protected static $shortName = 'findByIdentifier'; | ||
|
||
/** | ||
* {@inheritdoc} | ||
* | ||
* @var integer | ||
*/ | ||
protected static $priority = 100; | ||
|
||
/** | ||
* @Flow\Inject | ||
* @var ContentRepositoryRegistry | ||
*/ | ||
protected $contentRepositoryRegistry; | ||
|
||
/** | ||
* {@inheritdoc} | ||
* | ||
* @param array<int,mixed> $context (or array-like object) onto which this operation should be applied | ||
* @return boolean true if the operation can be applied onto the $context, false otherwise | ||
*/ | ||
public function canEvaluate($context) | ||
{ | ||
foreach ($context as $contextNode) { | ||
if (!$contextNode instanceof Node) { | ||
return false; | ||
} | ||
} | ||
|
||
return true; | ||
} | ||
/** | ||
* This operation operates rather on the given Context object than on the given node | ||
* and thus may work with the legacy node interface until subgraphs are available | ||
* {@inheritdoc} | ||
* | ||
* @param FlowQuery<int,mixed> $flowQuery the FlowQuery object | ||
* @param array<int,mixed> $arguments the arguments for this operation | ||
* @throws FlowQueryException | ||
* @throws \Neos\Eel\Exception | ||
* @throws \Neos\Eel\FlowQuery\FizzleException | ||
*/ | ||
public function evaluate(FlowQuery $flowQuery, array $arguments): void | ||
{ | ||
/** @var array<int,Node> $contextNodes */ | ||
$contextNodes = $flowQuery->getContext(); | ||
if (count($contextNodes) === 0 || empty($arguments[0])) { | ||
return; | ||
} | ||
|
||
$firstContextNode = reset($contextNodes); | ||
assert($firstContextNode instanceof Node); | ||
|
||
$nodeAggregateId = NodeAggregateId::fromString($arguments[0]); | ||
|
||
/** @var Node[] $result */ | ||
$result = []; | ||
|
||
/** @var Node $contextNode */ | ||
foreach ($flowQuery->getContext() as $contextNode) { | ||
$subgraph = $this->contentRepositoryRegistry->subgraphForNode($contextNode); | ||
$nodeByIdentifier = $subgraph->findNodeById($nodeAggregateId); | ||
if ($nodeByIdentifier) { | ||
$result[] = $nodeByIdentifier; | ||
} | ||
} | ||
|
||
$flowQuery->setContext($result); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters