Generic selectors
Exact matches only
Search in title
Search in content
Post Type Selectors
Working with Custom Configuration Files in Magento 2
Yaroslav Rogoza Avatar

As you may know, initially Magento keeps all configuration values in XML files. XML structure allows dividing all configuration values into separate sections, subsections, etc. In that way, every configuration value has its own path in the configuration three (called XPath). We usually work with Magento built-in configuration files, however, there are some situations when we need to have our own configuration file alongside with the standard files.For example, if we want all configuration values centralized but don’t want to allow modifications via the admin panel. Indeed, we could store all values in the database, but this would necessitate a separate set of business logic (models, resource models, etc.) that interacts with the database, leading to extra database requests. XML files perfectly fit the necessity to hold configuration values separately in this case.

As a rule, XML processing is a resource consuming operation. Magento handles this problem by caching XML configuration. When we register our custom XML file using standard tools, we also have our configuration cached by default and that’s one more benefit embracing the artistry of custom Magento development.

Let’s take as an example a case when we need to have a list of warehouses with a set of details in the configuration values: warehouse name, warehouse postcode. Our XML file may look like the following one:

<?xml version="1.0" ?>

<!-- file app/code/Atwix/Warehouses/etc/warehouses_list.xml -->

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xsi:noNamespaceSchemaLocation="warehouses_list.xsd">
    <warehouses_list>
        <warehouse>
            <name>West 110</name>
            <postcode>91910</postcode>
        </warehouse>
    </warehouses_list>
</config>

Like any other XML file, it will be placed in the /etc directory. In our example the module is called Atwix_Warehouses. Magento 2 also offers a handy tool: XML validation using XSD schema. Using the mentioned schema XML file can be validated in IDE and even on the runtime level. For the mentioned example the XSD schema for our file validation will be the following:

<?xml version="1.0" ?>

<!-- file app/code/Atwix/Warehouses/etc/warehouses_list.xsd -->

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xs:element name="config">
        <xs:complexType>
            <xs:sequence>
                <xs:element minOccurs="1" maxOccurs="unbounded" name="warehouses_list" type="warehousesList" />
            </xs:sequence>
        </xs:complexType>
    </xs:element>
    <xs:complexType name="warehousesList">
        <xs:sequence>
            <xs:element minOccurs="1" maxOccurs="unbounded" name="users" type="warehouse" />
        </xs:sequence>
    </xs:complexType>
    <xs:complexType name="warehouse">
        <xs:sequence>
            <xs:element minOccurs="1" maxOccurs="unbounded" name="user" type="warehouseData" />
        </xs:sequence>
    </xs:complexType>
    <xs:complexType name="warehouseData">
        <xs:sequence>
            <xs:element name="name" />
            <xs:element name="postcode" />
        </xs:sequence>
    </xs:complexType>
</xs:schema>

To make the XML file accessible via built in Magento tools (and make it cacheable as well) it’s necessary to initialize it in the Dependency Injection configuration (/etc/di.xml):

<!-- file app/code/Atwix/Warehouses/etc/di.xml -->

...

<virtualType name="WHListReader" type="Magento\Framework\Config\Reader\Filesystem">
    <arguments>
        <argument name="converter" xsi:type="object">Atwix\Warehouses\Model\Config\Converter</argument>
        <argument name="schemaLocator" xsi:type="object">Atwix\Warehouses\Model\Config\SchemaLocator</argument>
        <argument name="fileName" xsi:type="string">users_list.xml</argument>
    </arguments>
</virtualType>
<type name="Atwix\Warehouses\Model\Config\Data">
    <arguments>
        <argument name="reader" xsi:type="object">WHListReader</argument>
        <argument name="cacheId" xsi:type="string">atwix_warehouses_list_cache</argument>
    </arguments>
</type>

...

Every XML file registered in Magento 2 needs to have XML reader, XML converter and XSD schema locator to be processed correctly. XML Converter is a class that implements Magento\Framework\Config\ConverterInterface and contains a public method “convert” that is responsible for XML parsing into an array or any other format. Here is an example of the configuration converter:

 namespace Atwix\Warehouselist\Model\Config;

class Converter implements \Magento\Framework\Config\ConverterInterface
{
    public function convert($source)
    {
        $warehouses = $source->getElementsByTagName('warehouses_list');
        $warehouseInfo = [];
        $iterator = 0;
        foreach ($warehouses as $warehouse) {
            foreach ($warehouse->childNodes as $warehouseInfo) {
                $warehouseInfo[$iterator][$warehouseInfo->localName] = $warehouseInfo->textContent;
            }
            $iterator++;
        }

        // …

        return ['warehouses' => $warehouseInfo];
    }
}

As you can see, the converter in the example above simply reads the XML node with name “warehouses_list” and iterates through its components copying the values to a separate array. After the converting process has been finished we can access the configuration values using the resulting array.

XSD schema locator is a class that implements \Magento\Framework\Config\SchemaLocatorInterface and contains different information like a path to the schema etc. The code inside this class is more or less standard until we need to have some additional functionality for the schema locator. Here is an example of the XSD schema locator:

namespace Atwix\Warehouses\Model\Config;
use Magento\Framework\Config\SchemaLocatorInterface;
use Magento\Framework\Module\Dir;


class SchemaLocator implements SchemaLocatorInterface
{
    /**
     * XML schema for config file.
     */
    const CONFIG_FILE_SCHEMA = 'warehouses_list.xsd';

    /**
     * Path to corresponding XSD file with validation rules for merged config
     *
     * @var string
     */
    protected $schema = null;

    /**
     * Path to corresponding XSD file with validation rules for separate config files
     * @var string
     */
    protected $perFileSchema = null;

    /**
     * @param \Magento\Framework\Module\Dir\Reader $moduleReader
     */
    public function __construct(\Magento\Framework\Module\Dir\Reader $moduleReader)
    {
        $configDir = $moduleReader->getModuleDir(Dir::MODULE_ETC_DIR, 'Atwix_Warehouses');
        $this->schema = $configDir . DIRECTORY_SEPARATOR . self::CONFIG_FILE_SCHEMA;
        $this->perFileSchema = $configDir . DIRECTORY_SEPARATOR . self::CONFIG_FILE_SCHEMA;
    }

    /**
     * {@inheritdoc}
     */
    public function getSchema()
    {
        return $this->schema;
    }

    /**
     * {@inheritdoc}
     */
    public function getPerFileSchema()
    {
        return $this->perFileSchema;
    }
}

The XML reader itself is a class that extends \Magento\Framework\Config\Data (Atwix\Warehouses\Model\Config\Data) in the Dependency Injection XML example above. This class might be an empty class:

namespace Atwix\Warehouses\Model\Config;
use Magento\Framework\Config\Data as DataConfig;


class Data implements DataConfig
{
}

That’s it, from this moment you should be able to get values from the newly created configuration file (do not forget to clean the cache). To access the configuration values you need to inject the Atwix\Warehouses\Model\Config\Data class in the constructor of your class and use “get” method to access the data:

namespace Atwix\Warehouses\Service;
use Atwix\Warehouses\Model\Config\Data as WarehousesConfig;


class WService
{
    /** @var WarehousesConfig wsConfig */
    protected WarehousesConfig wsConfig;
    
    /** @var WarehousesConfig wsConfig */
    public function __construct(WarehousesConfig $wsConfig)
    {
        $this->wsConfig = $wsConfig;
    }
    
    public function getWsList()
    {
        return $this->wsConfig->get('warehouses');
    }
}

I Hope this post helps you start using your own configuration files in Magento 2 way. If you have any questions or ideas, feel free to leave your comments below. Thank you.

You may also be interested in: