Wie kann ich eine benutzerdefinierte XML-Datei in Modulen als eine in Magento 2 zusammenführen lassen? (MageStackDay-Rätselfrage 2)

22

MageStackDay Bonusfrage für 500 Punkte Kopfgeld UND die Möglichkeit, eine kostenlose Z-Ray-Lizenz für ein Jahr zu gewinnen. Weitere Infos finden Sie >> hier <<

Die Fragen wurden vom Magento 2-Kernentwickler Anton Kril gestellt / inspiriert.

Frage:

Ich erstelle eine Erweiterung mit einem separaten Satz von Konfigurationen.
Dies bedeutet, dass ich keine config.xmloder routes.xmloder fieldset.xmlandere Konfigurations-XML-Dateien von Magento verwenden kann.
Beispiel.

Angenommen, ich definiere eine 'Tabellen'-Konfiguration, die Zeilen und Spalten enthält. Ich könnte diese XML unten verwenden. (nenn es 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>

Aber wenn eine andere Erweiterung enthält, table.xmlmöchte ich, dass sie vom Konfigurationsleser abgeholt wird und die 2 oder mehr XML-Dateien zusammengeführt werden. Ich meine, wenn die zweite Datei so aussieht

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

Das Ergebnis ist, dass die zweite Spalte zur ersten Zeile hinzugefügt wird und der Wert für attr1von der zweiten XML-Datei überschrieben wird:

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

In Magento 1 hätte ich dies einfach durch einen Anruf tun können

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

Wie kann ich dasselbe für Magento 2 tun?

Sander Mangel
quelle

Antworten:

15

In Magento 2 wird dies von der \Magento\Framework\Config\Reader\FilesystemKlasse erledigt . Mit dieser Klasse können Sie die XML-Datei angeben, die Sie zusammenführen möchten.

Im folgenden Teil werden alle in den verfügbaren Modulen gefundenen Dateien zusammengeführt und die Ausgabe (Ausschnitt aus \Magento\Framework\Config\Reader\Filesystem) zusammengeführt.

/**
 * 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;
}

In der von mir erstellten Lösung wurde die obige Klasse erweitert, um die erforderliche XML-Datei bereitzustellen und anzugeben, wo sich die zu überprüfende XSD-Datei befindet (ein vollständiges Beispiel finden Sie unter 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
        );
    }

Um die zusammengeführten Daten abzurufen, können Sie Folgendes aufrufen:

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

Die Ausgabe ist dann eine Array-Darstellung der zusammengeführten XML.

BEARBEITEN:

Um zu testen, wie die Dateien gelesen werden, habe ich ein funktionierendes Beispiel erstellt (siehe https://github.com/Genmato/MageStackTable ). Die Antwort wurde mit dem Lösungsbuild aktualisiert.

Vladimir Kerkhoff
quelle
Vladimir, früher heute habe ich deine vorherige Antwortversion mit DomKlassenbeispiel gesehen. Ich fing an, unter Verwendung des ReaderUnterrichts an Antworten zu arbeiten . In der Zwischenzeit habe ich die
Fragenseite
Danke für die ausführliche Antwort und für das POC-Modul von github. Bitte lassen Sie es dort für zukünftige Referenzen. Hier ... hab ein Kopfgeld.
Marius
Marius, danke! Lässt das Modul auf GitHub verfügbar.
Vladimir Kerkhoff