Как я могу иметь собственный XML-файл в модулях, слитых как один в Magento 2? (MageStackDay, загадочный вопрос 2)


22

Бонусный вопрос MageStackDay для 500 Bounty И возможность выиграть бесплатную лицензию Z-Ray в течение года. Более подробную информацию можно найти здесь >>

Вопросы предоставлены / вдохновлены разработчиком ядра Magento 2 Антоном Крилом.

Вопрос:

Я создаю расширение, которое имеет отдельный набор конфигураций.
Это означает , что я не могу использовать config.xmlили routes.xmlили fieldset.xmlили любые другие конфигурации XML файлы Magento имеет.
Пример.

Допустим, я определяю конфигурацию 'table', в которой есть строки и столбцы. Я мог бы использовать этот XML ниже. (позвони table.xml)

<table xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="path/to/table.xsd">
    <row id="row1">
        <column id="col1" sort="10" attr1="val1">
            <label>Col 1</label>
        </column>
    </row>
    <row id="row2">
        <column id="col1" sort="10" attr1="val1">
            <label>Col 1</label>
        </column>
        <column id="col2" sort="20" disabled="true" attr1="val2" >
            <label>Col 2</label>
        </column>
        <column id="col3" sort="15" attr1="val1">
            <label>Col 3</label>
        </column>
    </row>
</table>

Но если другое расширение содержит, table.xmlя хочу, чтобы это было подхвачено читателем конфигурации, и 2 или больше файла XML должны быть объединены. Я имею в виду, если второй файл выглядит так

<table xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="path/to/table.xsd">
    <row id="row1">
        <column id="col2" sort="10" attr1="val2">
            <label>Col 2</label>
        </column>
    </row>
    <row id="row2">
        <column id="col1" sort="10" attr1="val5" />
    </row>
</table>

В результате второй столбец будет добавлен в первую строку, а значение для attr1будет перезаписано вторым xml:

<table ....>
    <row id="row1">
        <column id="col1" sort="10" attr1="val1"> <!-- from first xml -->
            <label>Col 1</label>
        </column>
        <column id="col2" sort="10" attr1="val2"><!-- from second xml-->
            <label>Col 2</label>
        </column>
    </row>
    <row id="row2">
        <column id="col1" sort="10" attr1="val5"><!--they apear in both xmls with the same path and id and second one overrides the value for `attr1`-->
            <label>Col 1</label>
        </column>
        <column id="col2" sort="20" disabled="true" attr1="val2"><!-- from first xml -->
            <label>Col 2</label>
        </column>
        <column id="col3" sort="15" attr1="val1"><!-- from first xml -->
            <label>Col 3</label>
        </column>
    </row>
</table>

В Magento 1 я мог бы сделать это, просто позвонив

 $merged = Mage::getConfig()->loadModulesConfiguration('table.xml')
            ->applyExtends();

Как я могу сделать то же самое для Magento 2?

Ответы:


15

В Magento 2 этим занимается \Magento\Framework\Config\Reader\Filesystemкласс. Этот класс позволяет вам указать XML-файл, который вы хотите объединить.

Следующая часть объединит все файлы, найденные в доступных модулях, и объединит выходные данные (фрагмент из \Magento\Framework\Config\Reader\Filesystem)

/**
 * Load configuration scope
 *
 * @param string|null $scope
 * @return array
 */
public function read($scope = null)
{
    $scope = $scope ?: $this->_defaultScope;
    $fileList = $this->_fileResolver->get($this->_fileName, $scope);
    if (!count($fileList)) {
        return [];
    }
    $output = $this->_readFiles($fileList);

    return $output;
}

/**
 * Read configuration files
 *
 * @param array $fileList
 * @return array
 * @throws \Magento\Framework\Exception
 */
protected function _readFiles($fileList)
{
    /** @var \Magento\Framework\Config\Dom $configMerger */
    $configMerger = null;
    foreach ($fileList as $key => $content) {
        try {
            if (!$configMerger) {
                $configMerger = $this->_createConfigMerger($this->_domDocumentClass, $content);
            } else {
                $configMerger->merge($content);
            }
        } catch (\Magento\Framework\Config\Dom\ValidationException $e) {
            throw new \Magento\Framework\Exception("Invalid XML in file " . $key . ":\n" . $e->getMessage());
        }
    }
    if ($this->_isValidated) {
        $errors = [];
        if ($configMerger && !$configMerger->validate($this->_schemaFile, $errors)) {
            $message = "Invalid Document \n";
            throw new \Magento\Framework\Exception($message . implode("\n", $errors));
        }
    }

    $output = [];
    if ($configMerger) {
        $output = $this->_converter->convert($configMerger->getDom());
    }
    return $output;
}

В решении, которое я создал, вышеприведенный класс расширен для предоставления необходимого xml-файла и указания, где можно найти xsd-файл для проверки (см. Https://github.com/Genmato/MageStackTable для полного примера):

namespace Genmato\TableXml\Model\Table;

class Reader extends \Magento\Framework\Config\Reader\Filesystem
{
    protected $_idAttributes = [
        '/table/row' => 'id',
        '/table/row/column' => 'id',
    ];

    /**
     * @param \Magento\Framework\Config\FileResolverInterface $fileResolver
     * @param \Magento\Framework\Config\ConverterInterface $converter
     * @param \Genmato\TableXml\Model\Table\SchemaLocator $schemaLocator
     * @param \Magento\Framework\Config\ValidationStateInterface $validationState
     * @param string $fileName
     * @param array $idAttributes
     * @param string $domDocumentClass
     * @param string $defaultScope
     */
    public function __construct(
        \Magento\Framework\Config\FileResolverInterface $fileResolver,
        \Magento\Framework\Config\ConverterInterface $converter,
        \Genmato\TableXml\Model\Table\SchemaLocator $schemaLocator,
        \Magento\Framework\Config\ValidationStateInterface $validationState,
        $fileName = 'table.xml',
        $idAttributes = [],
        $domDocumentClass = 'Magento\Framework\Config\Dom',
        $defaultScope = 'global'
    ) {
        parent::__construct(
            $fileResolver,
            $converter,
            $schemaLocator,
            $validationState,
            $fileName,
            $idAttributes,
            $domDocumentClass,
            $defaultScope
        );
    }

Чтобы получить объединенные данные, вы можете позвонить:

$output = $this->_objectManager->get('Genmato\TableXml\Model\Table\Reader')->read();

Результатом является представление массива объединенного XML.

РЕДАКТИРОВАТЬ:

Чтобы проверить способ чтения файлов, я создал рабочий пример (см. Https://github.com/Genmato/MageStackTable ). Обновлен ответ с решением сборки.


Владимир, ранее сегодня я видел ваш предыдущий вариант ответа с Domпримером класса. Я начал работать над ответом, используя Readerкласс. Тем временем я обновил страницу с вопросом и понял, что ты это сделал :-) +1
Войтек Нарунец

Спасибо за полный подробный ответ и за модуль POC от github. Пожалуйста, оставьте это там для будущих ссылок. Здесь ... есть щедрость.
Мариус

Мариус, спасибо! Оставим модуль доступным на GitHub.
Владимир Керхофф
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.