Як я можу мати власний XML-файл у модулях, об'єднаних як один у Magento 2? (Таємниче питання MageStackDay 2)


22

Бонусне питання MageStackDay за 500 бант Bounty та можливість виграти безкоштовну ліцензію Z-Ray на рік. Більше інформації можна знайти >> тут <<

Питання надає / надихає розробник ядра Magento 2 Антон Криль.

Питання:

Я створюю розширення, яке має окремий набір конфігурацій.
Це означає , що я не можу використовувати config.xmlабо routes.xmlчи fieldset.xmlабо будь-які інші конфігурації XML файли Magento має.
Приклад.

Скажімо, я визначаю конфігурацію "таблиця", що містить рядки стовпців. Я можу використовувати цей 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.