Jak mogę połączyć niestandardowy plik XML w modułach scalony jako jeden w Magento 2? (Tajemnicze pytanie MageStackDay 2)

22

Pytanie bonusowe MageStackDay za 500 punktów Bounty O możliwość wygrania bezpłatnej licencji Z-Ray na rok. Więcej informacji można znaleźć >> tutaj <<

Pytania zostały dostarczone / zainspirowane przez głównego programistę Magento 2, Anton Kril.

Pytanie:

Tworzę rozszerzenie, które ma osobny zestaw konfiguracji.
To znaczy, że nie można używać config.xmllub routes.xmllub fieldset.xmllub inne pliki config xml magento ma.
Przykład.

Powiedzmy, że definiuję konfigurację „tabeli”, która ma wiersze i kolumny. Mógłbym użyć tego xml poniżej. (nazwij to 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>

Ale jeśli inne rozszerzenie zawiera table.xml, chcę, aby zostało pobrane przez czytnik konfiguracji i 2 lub więcej plików XML powinno zostać scalonych. Mam na myśli to, czy drugi plik wygląda tak

<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>

w rezultacie druga kolumna zostanie dodana do pierwszego wiersza, a wartość dla attr1zostanie zastąpiona przez drugi 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>

W Magento 1 mogłem to zrobić, dzwoniąc

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

Jak mogę zrobić to samo dla Magento 2?

Sander Mangel
źródło

Odpowiedzi:

15

W Magento 2 jest to obsługiwane przez \Magento\Framework\Config\Reader\Filesystemklasę. Ta klasa pozwala określić plik XML, który chcesz scalić.

Następująca część scali wszystkie pliki znalezione w dostępnych modułach i scali dane wyjściowe (fragment kodu \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;
}

W rozwiązaniu, które utworzyłem, powyższa klasa została rozszerzona o dostarczenie potrzebnego pliku xml i określenie, gdzie można znaleźć plik xsd do sprawdzenia ( pełny przykład można znaleźć na 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
        );
    }

Aby uzyskać scalone dane, możesz zadzwonić:

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

Dane wyjściowe to reprezentacja tablicowa scalonego pliku XML.

EDYTOWAĆ:

Aby przetestować sposób odczytu plików, stworzyłem działający przykład (patrz https://github.com/Genmato/MageStackTable ). Zaktualizowano odpowiedź o kompilację rozwiązania.

Vladimir Kerkhoff
źródło
Vladimir, wcześniej dzisiaj widziałem twoją poprzednią wersję odpowiedzi z Domprzykładem klasy. Zacząłem pracować nad odpowiedzią, korzystając z Readerklasy. W międzyczasie odświeżyłem stronę pytań i zdałem sobie sprawę, że to zrobiłeś :-) +1
Wojtek Naruniec
Dzięki za pełną szczegółową odpowiedź i za moduł POC od github. Zostaw to tutaj do wykorzystania w przyszłości. Tutaj ... zdobądź nagrodę.
Marius
Marius, dzięki! Zostawi moduł dostępny na GitHub.
Vladimir Kerkhoff,