It is specifically implemented for being used inside Eel, the Embedded Expression
Language for Flow.
Essentially, a FlowQuery object is a container for an *ordered set* of objects
of a certain type. On this container, *operations* can be applied (like
filter(), children(), ...).
All of these operations work on a *set*, that is, an operation usually expands
or shrinks the set of objects.
An operation normally returns a new FlowQuery instance with the operation applied,
but there are also some operations like is(...) or count(), which return simple
types like boolean or numbers. We call these operations *final operations*.
Internal Workings
=================
To allow for optimization, calling operations are not immediately executed.
Instead, they are appended to a *list of operations*. Only if one tries to
iterate over the FlowQuery object or calls a final operation, the operations
get executed and the result is computed.
Implementation of Operations
----------------------------
Operations are implemented by implementing the {@link OperationInterface} or,
more commonly, subclassing the {@link Operations/AbstractOperation}.
An operation must be *equivalence preserving*, that is, the following equation
must always hold:
applyAllOperations(context) = applyRemainingOperations(applyOperation(context))
While an operation is running, it can *add new operations* to the front of the
operation queue (with {@link pushOperation()}), so for example count($filter)
can first apply filter($filter), followed by count(). However, when doing this,
great care must be applied by the operation developer in order to still have
finite runs, and to make sure the operation is *equivalence preserving*.
Furthermore, an operation can *pop* its following operations from the operation
stack, and *peek* what the next operation is. It is up to the operation developer
to ensure equivalence preservation.
An operation may *never* invoke __call() on the FlowQuery object it receives;
as this might lead to an undefined state; i.e. you are not allowed to do:
$flowQuery->someOtherOperation() *inside* an operation.
Final Operations
----------------
If an operation is final, it should return the resulting value directly.
/** * {@inheritdoc} * * @param FlowQuery $flowQuery the FlowQuery object * @param array $arguments the arguments for this operation * @return void */ public function evaluate(FlowQuery $flowQuery, array $arguments) { $output = array(); $outputNodePaths = array(); $until = array(); foreach ($flowQuery->getContext() as $contextNode) { $nextNodes = $this->getNextForNode($contextNode); if (isset($arguments[0]) && !empty($arguments[0])) { $untilQuery = new FlowQuery($nextNodes); $untilQuery->pushOperation('filter', array($arguments[0])); $until = $untilQuery->get(); } if (isset($until[0]) && !empty($until[0])) { $nextNodes = $this->getNodesUntil($nextNodes, $until[0]); } if (is_array($nextNodes)) { foreach ($nextNodes as $nextNode) { if ($nextNode !== null && !isset($outputNodePaths[$nextNode->getPath()])) { $outputNodePaths[$nextNode->getPath()] = true; $output[] = $nextNode; } } } } $flowQuery->setContext($output); if (isset($arguments[1]) && !empty($arguments[1])) { $flowQuery->pushOperation('filter', array($arguments[1])); } }
/** * Tests on a tree: * * a * a1 * a2 * b (TestingNodeType) * b1 (TestingNodeType) * b1a * b2 * b3 (TestingNodeTypeWithSubnodes) * b3a (TestingNodeType) * b3b * * @test * @dataProvider closestOperationDataProvider */ public function closestOperationTests($currentNodePath, $nodeTypeFilter, $expectedNodePath) { $nodeTypeManager = $this->objectManager->get(NodeTypeManager::class); $testNodeType1 = $nodeTypeManager->getNodeType('Neos.ContentRepository.Testing:NodeType'); $testNodeType2 = $nodeTypeManager->getNodeType('Neos.ContentRepository.Testing:NodeTypeWithSubnodes'); $rootNode = $this->node->getNode('/'); $nodeA = $rootNode->createNode('a'); $nodeA->createNode('a1'); $nodeA->createNode('a2'); $nodeB = $rootNode->createNode('b', $testNodeType1); $nodeB1 = $nodeB->createNode('b1', $testNodeType1); $nodeB1->createNode('b1a'); $nodeB->createNode('b2'); $nodeB3 = $nodeB->createNode('b3', $testNodeType2); $nodeB3->createNode('b3a', $testNodeType1); $nodeB3->createNode('b3b'); $currentNode = $rootNode->getNode($currentNodePath); $q = new FlowQuery(array($currentNode)); $actualNode = $q->closest($nodeTypeFilter)->get(0); if ($expectedNodePath === null) { if ($actualNode !== null) { $this->fail('Expected resulting node to be NULL'); } $this->assertNull($actualNode); } else { $this->assertSame($expectedNodePath, $actualNode->getPath()); } }
/** * Tests on a tree: * * a * a1 * b * b1 (TestingNodeType) * b1a * c * * @test * @dataProvider hasOperationDataProvider() */ public function hasOperationTests(array $currentNodePaths, $subject, array $expectedNodePaths) { $nodeTypeManager = $this->objectManager->get(NodeTypeManager::class); $testNodeType1 = $nodeTypeManager->getNodeType('Neos.ContentRepository.Testing:NodeType'); $rootNode = $this->node->getNode('/'); $nodeA = $rootNode->createNode('a'); $nodeA->createNode('a1'); $nodeB = $rootNode->createNode('b'); $nodeB1 = $nodeB->createNode('b1', $testNodeType1); $nodeB1->createNode('b1a'); $rootNode->createNode('c'); $currentNodes = array(); foreach ($currentNodePaths as $currentNodePath) { $currentNodes[] = $rootNode->getNode($currentNodePath); } if (is_array($subject)) { $subjectNodes = array(); foreach ($subject as $subjectNodePath) { $subjectNodes[] = $rootNode->getNode($subjectNodePath); } $subject = $subjectNodes; } $q = new FlowQuery($currentNodes); $result = $q->has($subject)->get(); if ($expectedNodePaths === array()) { $this->assertEmpty($result); } else { foreach ($expectedNodePaths as $expectedNodePath) { $expectedNode = $rootNode->getNode($expectedNodePath); if (!in_array($expectedNode, $result)) { $this->fail(sprintf('Expected result to contain node "%s"', $expectedNodePath)); } } } }
/** * {@inheritdoc} * * @param FlowQuery $flowQuery the FlowQuery object * @param array $arguments the arguments for this operation * @return void */ public function evaluate(FlowQuery $flowQuery, array $arguments) { $output = array(); $outputNodePaths = array(); foreach ($flowQuery->getContext() as $contextNode) { $siteNode = $contextNode->getContext()->getCurrentSiteNode(); $parentNodes = $this->getParents($contextNode, $siteNode); if (isset($arguments[0]) && !empty($arguments[0] && isset($parentNodes[0]))) { $untilQuery = new FlowQuery(array($parentNodes[0])); $untilQuery->pushOperation('closest', array($arguments[0])); $until = $untilQuery->get(); } if (isset($until) && is_array($until) && !empty($until) && isset($until[0])) { $parentNodes = $this->getNodesUntil($parentNodes, $until[0]); } if (is_array($parentNodes)) { foreach ($parentNodes as $parentNode) { if ($parentNode !== null && !isset($outputNodePaths[$parentNode->getPath()])) { $outputNodePaths[$parentNode->getPath()] = true; $output[] = $parentNode; } } } } $flowQuery->setContext($output); if (isset($arguments[1]) && !empty($arguments[1])) { $flowQuery->pushOperation('filter', $arguments[1]); } }
/** * @test */ public function parentsFollowedByFirstMatchesInnermostNodeOnRootline() { $teaserText = $this->node->getNode('teaser/dummy42'); $q = new FlowQuery(array($teaserText)); $actual = iterator_to_array($q->parents('[someSpecialProperty]')->first()); $expected = array($this->node->getNode('teaser')); $this->assertTrue($expected === $actual); }
/** * {@inheritdoc} * * @param FlowQuery $flowQuery the FlowQuery object * @param array $arguments Ignored for this operation * @return void */ public function evaluate(FlowQuery $flowQuery, array $arguments) { $context = $flowQuery->getContext(); if (count($context) > 0) { $flowQuery->setContext([end($context)]); } else { $flowQuery->setContext([]); } }
/** * {@inheritdoc} * * @param FlowQuery $flowQuery the FlowQuery object * @param array $arguments Ignored for this operation * @return void */ public function evaluate(FlowQuery $flowQuery, array $arguments) { $context = $flowQuery->getContext(); if (isset($context[0])) { $flowQuery->setContext([$context[0]]); } else { $flowQuery->setContext([]); } }
/** * {@inheritdoc} * * @param FlowQuery $flowQuery the FlowQuery object * @param array $arguments the filter arguments * @return void|boolean */ public function evaluate(FlowQuery $flowQuery, array $arguments) { if (count($arguments) == 0) { return count($flowQuery->getContext()) > 0; } else { $flowQuery->pushOperation('is', []); $flowQuery->pushOperation('filter', $arguments); } }
/** * {@inheritdoc} * * @param FlowQuery $flowQuery the FlowQuery object * @param array $arguments A mandatory start and optional end index in the context, negative indices indicate an offset from the start or end respectively * @return void */ public function evaluate(FlowQuery $flowQuery, array $arguments) { $context = $flowQuery->getContext(); if ($context instanceof \Iterator) { $context = iterator_to_array($context); } if (isset($arguments[0]) && isset($arguments[1])) { $context = array_slice($context, (int) $arguments[0], (int) $arguments[1] - (int) $arguments[0]); } elseif (isset($arguments[0])) { $context = array_slice($context, (int) $arguments[0]); } $flowQuery->setContext($context); }
/** * {@inheritdoc} * * @param FlowQuery $flowQuery the FlowQuery object * @param array $arguments the context index to fetch from * @return mixed */ public function evaluate(FlowQuery $flowQuery, array $arguments) { $context = $flowQuery->getContext(); if (isset($arguments[0])) { $index = $arguments[0]; if (isset($context[$index])) { return $context[$index]; } else { return null; } } else { return $context; } }
/** * {@inheritdoc} * * @param FlowQuery $flowQuery the FlowQuery object * @param array $arguments the property path to use (in index 0) * @return mixed */ public function evaluate(FlowQuery $flowQuery, array $arguments) { if (!isset($arguments[0]) || empty($arguments[0])) { throw new FlowQueryException('property() must be given an attribute name when used on objects, fetching all attributes is not supported.', 1332492263); } else { $context = $flowQuery->getContext(); if (!isset($context[0])) { return null; } $element = $context[0]; $propertyPath = $arguments[0]; return ObjectAccess::getPropertyPath($element, $propertyPath); } }
/** * @param array $nodes */ public function assignNodes(array $nodes) { $data = array(); foreach ($nodes as $node) { if ($node->getPath() !== '/') { $q = new FlowQuery(array($node)); $closestDocumentNode = $q->closest('[instanceof Neos.Neos:Document]')->get(0); if ($closestDocumentNode !== null) { $data[] = array('nodeContextPath' => $node->getContextPath(), 'documentNodeContextPath' => $closestDocumentNode->getContextPath()); } else { $this->systemLogger->log('You have a node that is no longer connected to a parent. Path: ' . $node->getPath() . ' (Identifier: ' . $node->getIdentifier() . ')'); } } } $this->assign('value', array('data' => $data, 'success' => true)); }
/** * {@inheritdoc} * * @param FlowQuery $flowQuery the FlowQuery object * @param array $arguments the arguments for this operation * @return void */ public function evaluate(FlowQuery $flowQuery, array $arguments) { $output = array(); $outputNodePaths = array(); foreach ($flowQuery->getContext() as $contextNode) { $prevNode = $this->getPrevForNode($contextNode); if ($prevNode !== null && !isset($outputNodePaths[$prevNode->getPath()])) { $outputNodePaths[$prevNode->getPath()] = true; $output[] = $prevNode; } } $flowQuery->setContext($output); if (isset($arguments[0]) && !empty($arguments[0])) { $flowQuery->pushOperation('filter', $arguments); } }
/** * {@inheritdoc} * * @param FlowQuery $flowQuery The FlowQuery object * @param array $arguments The arguments for this operation * @return void */ public function evaluate(FlowQuery $flowQuery, array $arguments) { if (!isset($arguments[0]) || !is_array($arguments[0])) { throw new FlowQueryException('context() requires an array argument of context properties', 1398030427); } $output = array(); foreach ($flowQuery->getContext() as $contextNode) { $contextProperties = $contextNode->getContext()->getProperties(); $modifiedContext = $this->contextFactory->create(array_merge($contextProperties, $arguments[0])); $nodeInModifiedContext = $modifiedContext->getNodeByIdentifier($contextNode->getIdentifier()); if ($nodeInModifiedContext !== null) { $output[$nodeInModifiedContext->getPath()] = $nodeInModifiedContext; } } $flowQuery->setContext(array_values($output)); }
/** * @param NodeInterface $node A node * @return array of document nodes */ public function render(NodeInterface $node) { $documentNodes = []; $flowQuery = new FlowQuery(array($node)); $nodes = array_reverse($flowQuery->parents('[instanceof Neos.Neos:Document]')->get()); /** @var NodeInterface $node */ foreach ($nodes as $documentNode) { $documentNodes[] = $documentNode; } if ($node->getNodeType()->isOfType('Neos.Neos:Document')) { $documentNodes[] = $node; } $this->templateVariableContainer->add('documentNodes', $documentNodes); $content = $this->renderChildren(); $this->templateVariableContainer->remove('documentNodes'); return $content; }
/** * {@inheritdoc} * * @param FlowQuery $flowQuery the FlowQuery object * @param array $arguments the elements to add (as array in index 0) * @return void */ public function evaluate(FlowQuery $flowQuery, array $arguments) { $output = []; foreach ($flowQuery->getContext() as $element) { $output[] = $element; } if (isset($arguments[0])) { if (is_array($arguments[0]) || $arguments[0] instanceof \Traversable) { foreach ($arguments[0] as $element) { $output[] = $element; } } else { $output[] = $arguments[0]; } } $flowQuery->setContext($output); }
/** * {@inheritdoc} * * @param FlowQuery $flowQuery the FlowQuery object * @param array $arguments the arguments for this operation * @return void */ public function evaluate(FlowQuery $flowQuery, array $arguments) { if (!isset($arguments[0]) || empty($arguments[0])) { throw new FlowQueryException('closest() requires a filter argument', 1332492263); } $output = array(); foreach ($flowQuery->getContext() as $contextNode) { $contextNodeQuery = new FlowQuery(array($contextNode)); $contextNodeQuery->pushOperation('first', array()); $contextNodeQuery->pushOperation('filter', $arguments); $parentsQuery = new FlowQuery(array($contextNode)); $contextNodeQuery->pushOperation('add', array($parentsQuery->parents($arguments[0])->get())); foreach ($contextNodeQuery as $result) { $output[$result->getPath()] = $result; } } $flowQuery->setContext(array_values($output)); }
/** * {@inheritdoc} * * @param FlowQuery $flowQuery the FlowQuery object * @param array $arguments the arguments for this operation * @return mixed */ public function evaluate(FlowQuery $flowQuery, array $arguments) { if (!isset($arguments[0]) || empty($arguments[0])) { throw new FlowQueryException('property() does not support returning all attributes yet', 1332492263); } else { $context = $flowQuery->getContext(); $propertyPath = $arguments[0]; if (!isset($context[0])) { return null; } $element = $context[0]; if ($propertyPath[0] === '_') { return ObjectAccess::getPropertyPath($element, substr($propertyPath, 1)); } else { return $element->getProperty($propertyPath); } } }
/** * {@inheritdoc} * * First argument is the node property to sort by. Works with internal arguments (_xyz) as well. * Second argument is the sort direction (ASC or DESC). * * @param FlowQuery $flowQuery the FlowQuery object * @param array $arguments the arguments for this operation. * @return mixed */ public function evaluate(FlowQuery $flowQuery, array $arguments) { $nodes = $flowQuery->getContext(); // Check sort property if (isset($arguments[0]) && !empty($arguments[0])) { $sortProperty = $arguments[0]; } else { throw new \Neos\Eel\FlowQuery\FlowQueryException('Please provide a node property to sort by.', 1467881104); } // Check sort direction if (isset($arguments[1]) && !empty($arguments[1]) && in_array(strtoupper($arguments[1]), ['ASC', 'DESC'])) { $sortOrder = strtoupper($arguments[1]); } else { throw new \Neos\Eel\FlowQuery\FlowQueryException('Please provide a valid sort direction (ASC or DESC)', 1467881105); } $sortedNodes = []; $sortSequence = []; $nodesByIdentifier = []; // Determine the property value to sort by /** @var Node $node */ foreach ($nodes as $node) { if ($sortProperty[0] === '_') { $propertyValue = \Neos\Utility\ObjectAccess::getPropertyPath($node, substr($sortProperty, 1)); } else { $propertyValue = $node->getProperty($sortProperty); } if ($propertyValue instanceof \DateTime) { $propertyValue = $propertyValue->getTimestamp(); } $sortSequence[$node->getIdentifier()] = $propertyValue; $nodesByIdentifier[$node->getIdentifier()] = $node; } // Create the sort sequence if ($sortOrder === 'DESC') { arsort($sortSequence); } elseif ($sortOrder === 'ASC') { asort($sortSequence); } // Build the sorted context that is returned foreach ($sortSequence as $nodeIdentifier => $value) { $sortedNodes[] = $nodesByIdentifier[$nodeIdentifier]; } $flowQuery->setContext($sortedNodes); }
/** * @Flow\Around("method(Neos\Flow\Mvc\Routing\UriBuilder->uriFor())") * @param \Neos\Flow\Aop\JoinPointInterface $joinPoint The current join point * @return string The result of the target method if it has not been intercepted */ public function rewritePluginViewUris(JoinPointInterface $joinPoint) { /** @var ActionRequest $request */ $request = $joinPoint->getProxy()->getRequest(); $arguments = $joinPoint->getMethodArguments(); $currentNode = $request->getInternalArgument('__node'); if (!$request->getMainRequest()->hasArgument('node') || !$currentNode instanceof Node) { return $joinPoint->getAdviceChain()->proceed($joinPoint); } $currentNode = $request->getInternalArgument('__node'); $controllerObjectName = $this->getControllerObjectName($request, $arguments); $actionName = $arguments['actionName'] !== null ? $arguments['actionName'] : $request->getControllerActionName(); $targetNode = $this->pluginService->getPluginNodeByAction($currentNode, $controllerObjectName, $actionName); // TODO override namespace $q = new FlowQuery(array($targetNode)); $pageNode = $q->closest('[instanceof Neos.Neos:Document]')->get(0); $result = $this->generateUriForNode($request, $joinPoint, $pageNode); return $result; }
/** * {@inheritdoc} * * @param FlowQuery $flowQuery * @param array $arguments * @return void */ public function evaluate(FlowQuery $flowQuery, array $arguments) { if (!isset($arguments[0]) || empty($arguments[0])) { return; } if ($arguments[0] instanceof NodeInterface) { $filteredContext = array(); $context = $flowQuery->getContext(); foreach ($context as $element) { if ($element === $arguments[0]) { $filteredContext[] = $element; break; } } $flowQuery->setContext($filteredContext); } else { parent::evaluate($flowQuery, $arguments); } }
/** * {@inheritdoc} * * @param FlowQuery $flowQuery the FlowQuery object * @param array $arguments the arguments for this operation * @return void */ public function evaluate(FlowQuery $flowQuery, array $arguments) { $output = array(); $outputNodePaths = array(); foreach ($flowQuery->getContext() as $contextNode) { /** @var NodeInterface $contextNode */ $siteNode = $contextNode->getContext()->getCurrentSiteNode(); while ($contextNode !== $siteNode && $contextNode->getParent() !== null) { $contextNode = $contextNode->getParent(); if (!isset($outputNodePaths[$contextNode->getPath()])) { $output[] = $contextNode; $outputNodePaths[$contextNode->getPath()] = true; } } } $flowQuery->setContext($output); if (isset($arguments[0]) && !empty($arguments[0])) { $flowQuery->pushOperation('filter', $arguments); } }
/** * {@inheritdoc} * * @param FlowQuery $flowQuery The FlowQuery object * @param array $arguments None * @return integer The cache lifetime in seconds or NULL if either no content collection was given or no child node had a "hiddenBeforeDateTime" or "hiddenAfterDateTime" property set */ public function evaluate(FlowQuery $flowQuery, array $arguments) { $minimumDateTime = null; foreach ($flowQuery->getContext() as $contextNode) { $hiddenBeforeDateTime = $contextNode->getHiddenBeforeDateTime(); if ($hiddenBeforeDateTime !== null && $hiddenBeforeDateTime > $this->now && ($minimumDateTime === null || $hiddenBeforeDateTime < $minimumDateTime)) { $minimumDateTime = $hiddenBeforeDateTime; } $hiddenAfterDateTime = $contextNode->getHiddenAfterDateTime(); if ($hiddenAfterDateTime !== null && $hiddenAfterDateTime > $this->now && ($minimumDateTime === null || $hiddenAfterDateTime < $minimumDateTime)) { $minimumDateTime = $hiddenAfterDateTime; } } if ($minimumDateTime !== null) { $maximumLifetime = $minimumDateTime->getTimestamp() - $this->now->getTimestamp(); if ($maximumLifetime > 0) { return $maximumLifetime; } } return null; }
/** * {@inheritdoc} * * @param FlowQuery $flowQuery the FlowQuery object * @param array $arguments the arguments for this operation * @return void */ public function evaluate(FlowQuery $flowQuery, array $arguments) { $output = array(); $outputNodePaths = array(); /** @var NodeInterface $contextNode */ foreach ($flowQuery->getContext() as $contextNode) { $outputNodePaths[$contextNode->getPath()] = true; } foreach ($flowQuery->getContext() as $contextNode) { $parentNode = $contextNode->getParent(); if ($parentNode instanceof NodeInterface) { foreach ($parentNode->getChildNodes() as $childNode) { if (!isset($outputNodePaths[$childNode->getPath()])) { $output[] = $childNode; $outputNodePaths[$childNode->getPath()] = true; } } } } $flowQuery->setContext($output); if (isset($arguments[0]) && !empty($arguments[0])) { $flowQuery->pushOperation('filter', $arguments); } }
/** * {@inheritdoc} * * @param FlowQuery $flowQuery the FlowQuery object * @param array $arguments the filter expression to use (in index 0) * @return void * @throws FizzleException */ public function evaluate(FlowQuery $flowQuery, array $arguments) { if (!isset($arguments[0]) || empty($arguments[0])) { return; } if (!is_string($arguments[0])) { throw new FizzleException('filter operation expects string argument', 1332489625); } $filter = $arguments[0]; $parsedFilter = FizzleParser::parseFilterGroup($filter); $filteredContext = []; $context = $flowQuery->getContext(); foreach ($context as $element) { if ($this->matchesFilterGroup($element, $parsedFilter)) { $filteredContext[] = $element; } } $flowQuery->setContext($filteredContext); }
/** * Takes care of creating a redirect to properly render the collection the given node is in. * * @param NodeInterface $node * @param string $typoScriptPath * @return string */ protected function redirectToRenderNode(NodeInterface $node, $typoScriptPath) { $q = new FlowQuery(array($node)); $closestContentCollection = $q->closest('[instanceof Neos.Neos:ContentCollection]')->get(0); $closestDocumentNode = $q->closest('[instanceof Neos.Neos:Document]')->get(0); $this->redirect('show', 'Frontend\\Node', 'Neos.Neos', ['node' => $closestDocumentNode, '__nodeContextPath' => $closestContentCollection->getContextPath(), '__affectedNodeContextPath' => $node->getContextPath(), '__typoScriptPath' => $typoScriptPath], 0, 303, 'html'); }
/** * Evaluate the property name filter by traversing to the child object. We only support * nested objects right now * * @param FlowQuery $query * @param string $propertyNameFilter * @return void */ protected function evaluatePropertyNameFilter(FlowQuery $query, $propertyNameFilter) { $resultObjects = []; $resultObjectHashes = []; foreach ($query->getContext() as $element) { $subProperty = ObjectAccess::getPropertyPath($element, $propertyNameFilter); if (is_object($subProperty) || is_array($subProperty)) { if (is_array($subProperty) || $subProperty instanceof \Traversable) { foreach ($subProperty as $childElement) { if (!isset($resultObjectHashes[spl_object_hash($childElement)])) { $resultObjectHashes[spl_object_hash($childElement)] = true; $resultObjects[] = $childElement; } } } elseif (!isset($resultObjectHashes[spl_object_hash($subProperty)])) { $resultObjectHashes[spl_object_hash($subProperty)] = true; $resultObjects[] = $subProperty; } } } $query->setContext($resultObjects); }
/** * {@inheritdoc} * * @param FlowQuery $flowQuery * @param array $arguments * @return void */ public function evaluate(FlowQuery $flowQuery, array $arguments) { $subject = $arguments[0]; if (!isset($subject) || empty($subject)) { $flowQuery->setContext(array()); return; } $filteredContext = array(); $context = $flowQuery->getContext(); if (is_string($subject)) { foreach ($context as $contextElement) { $contextElementQuery = new FlowQuery(array($contextElement)); $contextElementQuery->pushOperation('children', $arguments); if ($contextElementQuery->count() > 0) { $filteredContext[] = $contextElement; } } } else { if ($subject instanceof FlowQuery) { $elements = $subject->get(); } elseif ($subject instanceof \Traversable) { $elements = iterator_to_array($subject); } elseif (is_object($subject)) { $elements = array($subject); } elseif (is_array($subject)) { $elements = $subject; } else { throw new FizzleException('supplied argument for has operation not supported', 1332489625); } foreach ($elements as $element) { if ($element instanceof NodeInterface) { $parentsQuery = new FlowQuery(array($element)); /** @var NodeInterface $parent */ foreach ($parentsQuery->parents(array())->get() as $parent) { /** @var NodeInterface $contextElement */ foreach ($context as $contextElement) { if ($contextElement->getIdentifier() === $parent->getIdentifier()) { $filteredContext[] = $contextElement; } } } } } $filteredContext = array_unique($filteredContext); } $flowQuery->setContext($filteredContext); }
/** * Returns an array of usage reference objects. * * @param AssetInterface $asset * @return array<\Neos\Neos\Domain\Model\Dto\AssetUsageInNodeProperties> * @throws \Neos\ContentRepository\Exception\NodeConfigurationException */ public function getUsageReferences(AssetInterface $asset) { $assetIdentifier = $this->persistenceManager->getIdentifierByObject($asset); if (isset($this->firstlevelCache[$assetIdentifier])) { return $this->firstlevelCache[$assetIdentifier]; } $userWorkspace = $this->userService->getPersonalWorkspace(); $relatedNodes = []; foreach ($this->getRelatedNodes($asset) as $relatedNodeData) { $accessible = $this->domainUserService->currentUserCanReadWorkspace($relatedNodeData->getWorkspace()); if ($accessible) { $context = $this->createContextMatchingNodeData($relatedNodeData); } else { $context = $this->createContentContext($userWorkspace->getName()); } $site = $context->getCurrentSite(); $node = $this->nodeFactory->createFromNodeData($relatedNodeData, $context); $flowQuery = new FlowQuery([$node]); /** @var \Neos\ContentRepository\Domain\Model\NodeInterface $documentNode */ $documentNode = $flowQuery->closest('[instanceof Neos.Neos:Document]')->get(0); $relatedNodes[] = new AssetUsageInNodeProperties($asset, $site, $documentNode, $node, $accessible); } $this->firstlevelCache[$assetIdentifier] = $relatedNodes; return $this->firstlevelCache[$assetIdentifier]; }
/** * * @param string $workspaceName * @return NodeInterface */ protected function getLastVisitedNode($workspaceName) { if (!$this->session->isStarted() || !$this->session->hasKey('lastVisitedNode')) { return null; } try { $lastVisitedNode = $this->propertyMapper->convert($this->session->getData('lastVisitedNode'), NodeInterface::class); $q = new FlowQuery([$lastVisitedNode]); $lastVisitedNodeUserWorkspace = $q->context(['workspaceName' => $workspaceName])->get(0); return $lastVisitedNodeUserWorkspace; } catch (\Exception $exception) { return null; } }