123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366 |
- <?php
- /**
- * Copyright © Magento, Inc. All rights reserved.
- * See COPYING.txt for license details.
- */
- namespace Magento\Framework\Indexer\Config;
- use Magento\Framework\Config\ConverterInterface;
- use Magento\Framework\Exception\ConfigurationMismatchException;
- use Magento\Framework\Phrase;
- class Converter implements ConverterInterface
- {
- /**
- * Convert dom node tree to array
- *
- * @param \DOMDocument $source
- * @return array
- * @throws \InvalidArgumentException
- */
- public function convert($source)
- {
- $output = [];
- $xpath = new \DOMXPath($source);
- $indexers = $xpath->evaluate('/config/indexer');
- /** @var $typeNode \DOMNode */
- foreach ($indexers as $indexerNode) {
- $data = [];
- $indexerId = $this->getAttributeValue($indexerNode, 'id');
- $data['indexer_id'] = $indexerId;
- $data['primary'] = $this->getAttributeValue($indexerNode, 'primary');
- $data['view_id'] = $this->getAttributeValue($indexerNode, 'view_id');
- $data['action_class'] = $this->getAttributeValue($indexerNode, 'class');
- $data['shared_index'] = $this->getAttributeValue($indexerNode, 'shared_index');
- $data['title'] = '';
- $data['description'] = '';
- $data['dependencies'] = [];
- /** @var $childNode \DOMNode */
- foreach ($indexerNode->childNodes as $childNode) {
- if ($childNode->nodeType != XML_ELEMENT_NODE) {
- continue;
- }
- /** @var $childNode \DOMElement */
- $data = $this->convertChild($childNode, $data);
- }
- $output[$indexerId] = $data;
- }
- $output = $this->sortByDependencies($output);
- return $output;
- }
- /**
- * Get attribute value
- *
- * @param \DOMNode $input
- * @param string $attributeName
- * @param mixed $default
- * @return null|string
- */
- protected function getAttributeValue(\DOMNode $input, $attributeName, $default = null)
- {
- $node = $input->attributes->getNamedItem($attributeName);
- return $node ? $node->nodeValue : $default;
- }
- /**
- * Convert child from dom to array
- *
- * @param \DOMElement $childNode
- * @param array $data
- * @return array
- */
- protected function convertChild(\DOMElement $childNode, $data)
- {
- $data['fieldsets'] = isset($data['fieldsets']) ? $data['fieldsets'] : [];
- switch ($childNode->nodeName) {
- case 'title':
- $data['title'] = $childNode->nodeValue;
- break;
- case 'description':
- $data['description'] = $childNode->nodeValue;
- break;
- case 'saveHandler':
- $data['saveHandler'] = $this->getAttributeValue($childNode, 'class');
- break;
- case 'structure':
- $data['structure'] = $this->getAttributeValue($childNode, 'class');
- break;
- case 'fieldset':
- $data = $this->convertFieldset($childNode, $data);
- break;
- case 'dependencies':
- $data = $this->convertDependencies($childNode, $data);
- break;
- }
- return $data;
- }
- /**
- * Convert fieldset
- *
- * @param \DOMElement $node
- * @param array $data
- * @return array
- * @SuppressWarnings(PHPMD.NPathComplexity)
- */
- protected function convertFieldset(\DOMElement $node, $data)
- {
- $data['fieldsets'] = isset($data['fieldsets']) ? $data['fieldsets'] : [];
- $data['fieldsets'][$this->getAttributeValue($node, 'name')] = [
- 'source' => $this->getAttributeValue($node, 'source'),
- 'name' => $this->getAttributeValue($node, 'name'),
- 'provider' => $this->getAttributeValue($node, 'provider'),
- 'fields' => [],
- ];
- foreach ($node->childNodes as $childNode) {
- if ($childNode->nodeType != XML_ELEMENT_NODE) {
- continue;
- }
- switch ($childNode->nodeName) {
- case 'field':
- $data['fieldsets'][$this->getAttributeValue($node, 'name')] = $this->convertField(
- $childNode,
- $data['fieldsets'][$this->getAttributeValue($node, 'name')]
- );
- break;
- case 'reference':
- $data['fieldsets'][$this->getAttributeValue($node, 'name')]['references'] =
- isset($data['fieldsets'][$this->getAttributeValue($node, 'name')]['references'])
- ? $data['fieldsets'][$this->getAttributeValue($node, 'name')]['references']
- : [];
- $data['fieldsets'][$this->getAttributeValue($node, 'name')]['references']
- [$this->getAttributeValue($childNode, 'fieldset')] =
- isset(
- $data['fieldsets'][$this->getAttributeValue($node, 'name')]
- ['references'][$this->getAttributeValue($childNode, 'fieldset')]
- )
- ? $data['fieldsets'][$this->getAttributeValue($node, 'name')]
- ['references'][$this->getAttributeValue($childNode, 'fieldset')]
- : [];
- $data['fieldsets'][$this->getAttributeValue($node, 'name')]['references']
- [$this->getAttributeValue($childNode, 'fieldset')] = [
- 'fieldset' => $this->getAttributeValue($childNode, 'fieldset'),
- 'from' => $this->getAttributeValue($childNode, 'from'),
- 'to' => $this->getAttributeValue($childNode, 'to'),
- ];
- $this->addVirtualField(
- $this->getAttributeValue($childNode, 'fieldset'),
- $this->getAttributeValue($childNode, 'to'),
- $data
- );
- $this->addVirtualField(
- $this->getAttributeValue($node, 'name'),
- $this->getAttributeValue($childNode, 'from'),
- $data
- );
- break;
- }
- }
- return $this->sorting($data);
- }
- /**
- * Convert dependencies node
- *
- * @param \DOMElement $node
- * @param array $data
- * @return array
- */
- private function convertDependencies(\DOMElement $node, array $data): array
- {
- $data['dependencies'] = $data['dependencies'] ?? [];
- /** @var $childNode \DOMNode */
- foreach ($node->childNodes as $childNode) {
- switch ($childNode->nodeName) {
- case 'indexer':
- $indexerId = $this->getAttributeValue($childNode, 'id');
- $data['dependencies'][] = $indexerId;
- break;
- }
- }
- return $data;
- }
- /**
- * Add virtual field
- *
- * @param string $fieldset
- * @param string $field
- * @param array $data
- * @return void
- */
- protected function addVirtualField($fieldset, $field, $data)
- {
- if (!isset($data['fieldsets'][$fieldset]['fields'][$field])) {
- $data['fieldsets'][$fieldset]['fields'][$field] = [
- 'type' => 'virtual',
- 'name' => $field,
- ];
- }
- }
- /**
- * Convert field
- *
- * @param \DOMElement $node
- * @param array $data
- * @return array
- */
- protected function convertField(\DOMElement $node, $data)
- {
- $data['fields'][$this->getAttributeValue($node, 'name')] = [
- 'name' => $this->getAttributeValue($node, 'name'),
- 'handler' => $this->getAttributeValue($node, 'handler'),
- 'origin' => $this->getAttributeValue($node, 'origin') ?: $this->getAttributeValue($node, 'name'),
- 'dataType' => $this->getAttributeValue($node, 'dataType'),
- 'type' => $node->getAttributeNS('http://www.w3.org/2001/XMLSchema-instance', 'type'),
- ];
- $data['fields'][$this->getAttributeValue($node, 'name')]['filters'] = [];
- /** @var $childNode \DOMNode */
- foreach ($node->childNodes as $childNode) {
- if ($childNode->nodeType != XML_ELEMENT_NODE) {
- continue;
- }
- $data['fields'][$this->getAttributeValue($node, 'name')]['filters'][]
- = $this->getAttributeValue($childNode, 'class');
- }
- return $data;
- }
- /**
- * Return node value translated if applicable
- *
- * @param \DOMNode $node
- * @return string
- * @deprecated 101.0.0
- */
- protected function getTranslatedNodeValue(\DOMNode $node)
- {
- $value = $node->nodeValue;
- if ($this->getAttributeValue($node, 'translate') == 'true') {
- $value = new \Magento\Framework\Phrase($value);
- }
- return $value;
- }
- /**
- * Sorting fieldset
- *
- * @param array $data
- * @return array
- */
- protected function sorting($data)
- {
- usort($data['fieldsets'], function ($current, $parent) use ($data) {
- if (!isset($current['references']) && $data['primary'] == $current['name']
- || isset($parent['references'][$current['name']])
- ) {
- return -1;
- } elseif (!isset($parent['references']) || isset($current['references'][$parent['name']])) {
- return 1;
- } else {
- return 0;
- }
- });
- return $data;
- }
- /**
- * Sort the list of indexers using "dependencies" node data.
- *
- * This method also sort data in the "dependencies" node of indexers.
- *
- * @param array $indexers
- * @return array
- */
- private function sortByDependencies(array $indexers): array
- {
- $expanded = [];
- foreach (array_keys($indexers) as $indexerId) {
- $expanded[] = [
- 'indexerId' => $indexerId,
- 'dependencies' => $this->expandDependencies($indexers, $indexerId),
- ];
- }
- /**
- * Used this algorithm of sorting not quicksort, because it guarantees
- * correct sequence of indexers with multiple dependencies.
- */
- $maxIndex = count($expanded) - 1;
- for ($i = 0; $i < $maxIndex; $i++) {
- for ($j = $i + 1; $j <= $maxIndex; $j++) {
- if (in_array($expanded[$j]['indexerId'], $expanded[$i]['dependencies'], true)) {
- $temp = $expanded[$i];
- $expanded[$i] = $expanded[$j];
- $expanded[$j] = $temp;
- }
- }
- }
- $orderedIndexerIds = array_map(
- function ($item) {
- return $item['indexerId'];
- },
- $expanded
- );
- $result = [];
- foreach ($orderedIndexerIds as $indexerId) {
- $result[$indexerId] = $indexers[$indexerId];
- $result[$indexerId]['dependencies'] = array_values(
- array_intersect($orderedIndexerIds, $result[$indexerId]['dependencies'] ?? [])
- );
- }
- return $result;
- }
- /**
- * Accumulate information about all transitive "dependencies" references.
- *
- * @param array $list
- * @param string $indexerId
- * @param array $accumulated
- * @return array
- * @throws ConfigurationMismatchException
- */
- private function expandDependencies(array $list, string $indexerId, array $accumulated = []): array
- {
- $accumulated[] = $indexerId;
- $result = $list[$indexerId]['dependencies'] ?? [];
- foreach ($result as $relatedIndexerId) {
- if (in_array($relatedIndexerId, $accumulated)) {
- throw new ConfigurationMismatchException(
- new Phrase(
- "Circular dependency references from '%indexerId' to '%relatedIndexerId'.",
- [
- 'indexerId' => $indexerId,
- 'relatedIndexerId' => $relatedIndexerId,
- ]
- )
- );
- }
- if (!isset($list[$relatedIndexerId])) {
- throw new ConfigurationMismatchException(
- new Phrase(
- "Dependency declaration '%relatedIndexerId' in "
- . "'%indexerId' to the non-existing indexer.",
- [
- 'indexerId' => $indexerId,
- 'relatedIndexerId' => $relatedIndexerId,
- ]
- )
- );
- }
- $relatedResult = $this->expandDependencies($list, $relatedIndexerId, $accumulated);
- $result = array_unique(array_merge($result, $relatedResult));
- }
- return $result;
- }
- }
|