Bagaimana saya bisa memiliki file XML khusus dalam modul digabung sebagai satu di Magento 2? (Pertanyaan misteri MageStackDay 2)

22

Pertanyaan bonus MageStackDay untuk hadiah 500pts DAN kemungkinan memenangkan lisensi Z-Ray gratis selama setahun. Info lebih lanjut dapat ditemukan >> di sini <<

Pertanyaan diberikan / diinspirasi oleh pengembang inti Magento 2, Anton Kril.

Pertanyaan:

Saya membuat ekstensi yang memiliki serangkaian konfigurasi terpisah.
Ini berarti saya tidak dapat menggunakan config.xmlatau routes.xmlatau fieldset.xmlfile konfigurasi xml lainnya yang dimiliki magento.
Contoh.

Katakanlah saya mendefinisikan konfigurasi 'tabel' yang memiliki baris kolom. Saya dapat menggunakan xml ini di bawah. (sebut saja 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>

Tetapi jika ekstensi lain berisi table.xmlsaya ingin itu diambil oleh pembaca konfigurasi dan 2 atau lebih file xml harus digabung. Maksud saya jika file kedua terlihat seperti ini

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

hasilnya adalah kolom kedua ditambahkan ke baris pertama dan nilai untuk attr1ditimpa oleh xml kedua:

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

Di Magento 1 saya bisa melakukan ini hanya dengan menelepon

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

Bagaimana saya bisa melakukan hal yang sama untuk Magento 2?

Sander Mangel
sumber

Jawaban:

15

Di Magento 2 ini ditangani oleh \Magento\Framework\Config\Reader\Filesystemkelas. Kelas ini memungkinkan Anda menentukan file xml yang ingin Anda gabungkan.

Bagian berikut ini akan menggabungkan semua file yang ditemukan dalam modul yang tersedia dan menggabungkan output (potongan dari \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;
}

Dalam solusi yang saya buat, kelas di atas diperluas untuk memasok file xml yang diperlukan dan menentukan di mana file xsd untuk memvalidasi dapat ditemukan (lihat https://github.com/Genmato/MageStackTable untuk contoh lengkap):

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

Untuk mendapatkan data yang digabungkan, Anda dapat menghubungi:

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

Outputnya kemudian merupakan representasi array dari xml gabungan.

EDIT:

Untuk menguji cara file dibaca, saya membuat contoh yang berfungsi (lihat https://github.com/Genmato/MageStackTable ). Memperbarui jawabannya dengan build solusi.

Vladimir Kerkhoff
sumber
Vladimir, sebelumnya hari ini saya melihat versi jawaban Anda sebelumnya dengan Domcontoh kelas. Saya mulai mengerjakan jawaban memanfaatkan Readerkelas. Sementara itu saya menyegarkan halaman pertanyaan dan menyadari Anda melakukannya :-) +1
Wojtek Naruniec
Terima kasih atas jawaban terperinci dan untuk modul POC dari github. Silakan tinggalkan di sana untuk referensi di masa mendatang. Di sini ... memiliki karunia.
Marius
Marius, terima kasih! Akan meninggalkan modul yang tersedia di GitHub.
Vladimir Kerkhoff