Come posso avere un file XML personalizzato nei moduli unito come uno in Magento 2? (MageStackDay mystery question 2)


22

Domanda bonus MageStackDay per 500 punti Bounty E la possibilità di vincere una licenza Z-Ray gratuita per un anno. Maggiori informazioni possono essere trovate >> qui <<

Le domande sono fornite / ispirate dallo sviluppatore principale Magento 2 Anton Kril.

Domanda:

Sto creando un'estensione che ha un set separato di configurazioni.
Questo significa che non possono utilizzare config.xmlo routes.xmlo fieldset.xmlo qualsiasi altro file di configurazione XML Magento ha.
Esempio.

Diciamo che sto definendo una configurazione 'table' che ha righe e colonne. Potrei usare questo xml di seguito. (chiamalo 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>

Ma se contiene un'altra estensione, table.xmlvoglio che sia presa dal lettore di configurazione e che i 2 o più file XML debbano essere uniti. Voglio dire se il secondo file è simile al seguente

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

il risultato sarà che la seconda colonna verrà aggiunta alla prima riga e il valore per attr1verrà sovrascritto dal secondo 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>

In Magento 1 avrei potuto farlo semplicemente chiamando

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

Come posso fare lo stesso per Magento 2?

Risposte:


15

In Magento 2 questo è gestito dalla \Magento\Framework\Config\Reader\Filesystemclasse. Questa classe consente di specificare il file xml che si desidera unire.

La parte seguente unirà tutti i file trovati nei moduli disponibili e unirà l'output (frammento di \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;
}

Nella soluzione che ho creato la classe sopra è estesa per fornire il file xml necessario e specificare dove si trova il file xsd da validare (vedi https://github.com/Genmato/MageStackTable per un esempio completo):

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
        );
    }

Per ottenere i dati uniti puoi quindi chiamare:

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

L'output è quindi una rappresentazione in array dell'xml unito.

MODIFICARE:

Per testare la lettura dei file ho creato un esempio funzionante (consultare https://github.com/Genmato/MageStackTable ). Aggiornata la risposta con la build della soluzione.


Vladimir, oggi ho visto la tua versione di risposta precedente con un Domesempio di classe. Ho iniziato a lavorare sulla risposta utilizzando la Readerclasse. Nel frattempo ho aggiornato la pagina delle domande e ho capito che l'hai fatto :-) +1
Wojtek Naruniec,

Grazie per la risposta completa e dettagliata e per il modulo POC di github. Si prega di lasciarlo lì per riferimenti futuri. Ecco ... abbi un po 'di grazia.
Marius

Marius, grazie! Lascerà il modulo disponibile su GitHub.
Vladimir Kerkhoff,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.