Comment puis-je avoir un fichier XML personnalisé dans des modules fusionnés en un seul dans Magento 2? (Question mystère MageStackDay 2)

22

Question bonus MageStackDay pour 500pts Bounty ET la possibilité de gagner une licence Z-Ray gratuite pendant un an. Plus d'informations peuvent être trouvées >> ici <<

Les questions sont fournies / inspirées par le développeur principal de Magento 2, Anton Kril.

Question:

Je crée une extension qui a un ensemble distinct de configurations.
Cela signifie que je ne peux pas utiliser config.xmlou routes.xmlou fieldset.xmlou tout autre fichier de configuration xml que magento possède.
Exemple.

Disons que je définis une configuration de «table» qui a des lignes et des colonnes. Je pourrais utiliser ce xml ci-dessous. (appelez ça 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>

Mais si une autre extension contient, table.xmlje veux qu'elle soit récupérée par le lecteur de configuration et les 2 fichiers XML ou plus doivent être fusionnés. Je veux dire, si le deuxième fichier ressemble à ceci

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

le résultat sera que la deuxième colonne est ajoutée à la première ligne et la valeur de attr1est remplacée par le deuxième 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>

Dans Magento 1, j'aurais pu le faire en appelant

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

Comment puis-je faire de même pour Magento 2?

Sander Mangel
la source

Réponses:

15

Dans Magento 2, cela est géré par le \Magento\Framework\Config\Reader\Filesystem classe. Cette classe vous permet de spécifier le fichier xml que vous souhaitez fusionner.

La partie suivante fusionnera tous les fichiers trouvés dans les modules disponibles et fusionnera la sortie (extrait de \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;
}

Dans la solution que j'ai créée, la classe ci-dessus est étendue pour fournir le fichier xml nécessaire et spécifier où le fichier xsd à valider peut être trouvé (voir https://github.com/Genmato/MageStackTable pour un exemple complet):

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

Pour obtenir les données fusionnées, vous pouvez alors appeler:

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

La sortie est alors une représentation matricielle du XML fusionné.

MODIFIER:

Pour tester la façon dont les fichiers sont lus, j'ai créé un exemple de travail (voir https://github.com/Genmato/MageStackTable ). Mise à jour de la réponse avec la construction de la solution.

Vladimir Kerkhoff
la source
Vladimir, plus tôt dans la journée, j'ai vu votre version de réponse précédente avec Domun exemple de classe. J'ai commencé à travailler sur la réponse en utilisant la Readerclasse. En attendant, j'ai rafraîchi la page de questions et j'ai réalisé que vous l'aviez fait :-) +1
Wojtek Naruniec
Merci pour la réponse complète et détaillée et pour le module POC de github. Veuillez le laisser là pour de futures références. Ici ... ayez de la générosité.
Marius
Marius, merci! Laissera le module disponible sur GitHub.
Vladimir Kerkhoff