/** * {@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])); } }
/** * {@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]); } }
/** * {@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 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 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 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); } }
/** * {@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 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 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)); }
/** * {@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); }
/** * {@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); }
/** * {@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 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); }
/** * 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); }
/** * @test */ public function constructWithFlowQueryIsIdempotent() { $flowQuery = new FlowQuery(['a', 'b', 'c']); $wrappedQuery = new FlowQuery($flowQuery); $this->assertEquals($flowQuery->getContext(), $wrappedQuery->getContext()); }
/** * Optimize for typical use cases, filter by node name and filter * by NodeType (instanceof). These cases are now optimized and will * only load the nodes that match the filters. * * @param FlowQuery $flowQuery * @param array $parsedFilter * @return boolean */ protected function earlyOptimizationOfFilters(FlowQuery $flowQuery, array $parsedFilter) { $optimized = false; $output = array(); $outputNodePaths = array(); foreach ($parsedFilter['Filters'] as $filter) { $instanceOfFilters = array(); $attributeFilters = array(); if (isset($filter['AttributeFilters'])) { foreach ($filter['AttributeFilters'] as $attributeFilter) { if ($attributeFilter['Operator'] === 'instanceof' && $attributeFilter['Identifier'] === null) { $instanceOfFilters[] = $attributeFilter; } else { $attributeFilters[] = $attributeFilter; } } } // Only apply optimization if there's a property name filter or a instanceof filter or another filter already did optimization if (isset($filter['PropertyNameFilter']) || isset($filter['PathFilter']) || count($instanceOfFilters) > 0 || $optimized === true) { $optimized = true; $filteredOutput = array(); $filteredOutputNodePaths = array(); // Optimize property name filter if present if (isset($filter['PropertyNameFilter']) || isset($filter['PathFilter'])) { $nodePath = isset($filter['PropertyNameFilter']) ? $filter['PropertyNameFilter'] : $filter['PathFilter']; /** @var NodeInterface $contextNode */ foreach ($flowQuery->getContext() as $contextNode) { $childNode = $contextNode->getNode($nodePath); if ($childNode !== null && !isset($filteredOutputNodePaths[$childNode->getPath()])) { $filteredOutput[] = $childNode; $filteredOutputNodePaths[$childNode->getPath()] = true; } } } elseif (count($instanceOfFilters) > 0) { // Optimize node type filter if present $allowedNodeTypes = array_map(function ($instanceOfFilter) { return $instanceOfFilter['Operand']; }, $instanceOfFilters); /** @var NodeInterface $contextNode */ foreach ($flowQuery->getContext() as $contextNode) { /** @var NodeInterface $childNode */ foreach ($contextNode->getChildNodes(implode($allowedNodeTypes, ',')) as $childNode) { if (!isset($filteredOutputNodePaths[$childNode->getPath()])) { $filteredOutput[] = $childNode; $filteredOutputNodePaths[$childNode->getPath()] = true; } } } } // Apply attribute filters if present if (isset($filter['AttributeFilters'])) { $attributeFilters = array_reduce($filter['AttributeFilters'], function ($filters, $attributeFilter) { return $filters . $attributeFilter['text']; }); $filteredFlowQuery = new FlowQuery($filteredOutput); $filteredFlowQuery->pushOperation('filter', array($attributeFilters)); $filteredOutput = $filteredFlowQuery->get(); } // Add filtered nodes to output foreach ($filteredOutput as $filteredNode) { if (!isset($outputNodePaths[$filteredNode->getPath()])) { $output[] = $filteredNode; } } } } if ($optimized === true) { $flowQuery->setContext($output); } return $optimized; }
/** * {@inheritdoc} * * @param FlowQuery $flowQuery the FlowQuery object * @param array $arguments the arguments for this operation * @return void */ public function evaluate(FlowQuery $flowQuery, array $arguments) { $context = $flowQuery->getContext(); if (!isset($context[0]) || empty($arguments[0])) { return; } $result = array(); $selectorAndFilter = $arguments[0]; $parsedFilter = null; $parsedFilter = FizzleParser::parseFilterGroup($selectorAndFilter); if (isset($parsedFilter['Filters']) && $this->hasOnlyInstanceOfFilters($parsedFilter['Filters'])) { $nodeTypes = array(); foreach ($parsedFilter['Filters'] as $filter) { $nodeTypes[] = $filter['AttributeFilters'][0]['Operand']; } /** @var NodeInterface $contextNode */ foreach ($context as $contextNode) { $result = array_merge($result, $this->nodeDataRepository->findByParentAndNodeTypeInContext($contextNode->getPath(), implode(',', $nodeTypes), $contextNode->getContext(), true)); } } else { foreach ($parsedFilter['Filters'] as $filter) { $filterResults = array(); $generatedNodes = false; if (isset($filter['IdentifierFilter'])) { if (!preg_match(UuidValidator::PATTERN_MATCH_UUID, $filter['IdentifierFilter'])) { throw new FlowQueryException('find() requires a valid identifier', 1332492263); } /** @var NodeInterface $contextNode */ foreach ($context as $contextNode) { $filterResults = array($contextNode->getContext()->getNodeByIdentifier($filter['IdentifierFilter'])); } $generatedNodes = true; } elseif (isset($filter['PropertyNameFilter']) || isset($filter['PathFilter'])) { $nodePath = isset($filter['PropertyNameFilter']) ? $filter['PropertyNameFilter'] : $filter['PathFilter']; foreach ($context as $contextNode) { $node = $contextNode->getNode($nodePath); if ($node !== null) { array_push($filterResults, $node); } } $generatedNodes = true; } if (isset($filter['AttributeFilters']) && $filter['AttributeFilters'][0]['Operator'] === 'instanceof') { foreach ($context as $contextNode) { $filterResults = array_merge($filterResults, $this->nodeDataRepository->findByParentAndNodeTypeInContext($contextNode->getPath(), $filter['AttributeFilters'][0]['Operand'], $contextNode->getContext(), true)); } unset($filter['AttributeFilters'][0]); $generatedNodes = true; } if (isset($filter['AttributeFilters']) && count($filter['AttributeFilters']) > 0) { if (!$generatedNodes) { throw new FlowQueryException('find() needs an identifier, path or instanceof filter for the first filter part', 1436884196); } $filterQuery = new FlowQuery($filterResults); foreach ($filter['AttributeFilters'] as $attributeFilter) { $filterQuery->pushOperation('filter', array($attributeFilter['text'])); } $filterResults = $filterQuery->get(); } $result = array_merge($result, $filterResults); } } $flowQuery->setContext(array_unique($result)); }