Magento 2 – How Module Developers Should Read Their Own Configuration Files

areaconfigurationmagento2PHP

Scenario: I am a Magento 2 module developer. I want to create a configuration file in app/etc. I want this file to be "scoped" by area

app/etc/my_file.xml
app/etc/frontend/my_file.xml
app/etc/adminhtml/my_file.xml

In Magento 1 I'd just create a config.xmland be on my way. The area scoping happened in the XML file itself. However, Magento 2 approaches this very differently

In Magento 2, what class file(s?) should I create for reading these scoped configuration files. It's not clear from the Magento 2 source what "the right" way to do this is. The core code take multiple approaches, and none of them are marked with an @api method. This makes it difficult to know how to proceed with this common module developer task. As a secondary side-effect, it also makes it difficult to know how a Magento module developer should read from core configuration files.

On one hand, it seems like "the right" thing to do is create a file system reader object. For example, Magento seems to load the import.xml file with the following

#File: vendor/magento/module-import-export/Model/Import/Config/Reader.php
namespace Magento\ImportExport\Model\Import\Config;

class Reader extends \Magento\Framework\Config\Reader\Filesystem
{

    public function __construct(
        //...
        $fileName = 'import.xml',
        //...
    ) {
        parent::__construct(
            $fileResolver,
            $converter,
            $schemaLocator,
            $validationState,
            $fileName,
            $idAttributes,
            $domDocumentClass,
            $defaultScope
        );
    }
    //...
}        

The base Magento\Framework\Config\Reader\Filesystem class looks like it has code to resolve the area scope.

However some of the Magento configuration files seem to eschew this pattern. While there are readers for these files (event.xml in this example)

vendor/magento/framework/Event/Config/Reader.php

There's also "scoped data" classes that use these readers.

#File: vendor/magento/framework/Event/Config/Data.php
class Data extends \Magento\Framework\Config\Data\Scoped
{
    public function __construct(
        \Magento\Framework\Event\Config\Reader $reader,
        //...
    ) {
        parent::__construct($reader, $configScope, $cache, $cacheId);
    }
}

This makes it seem like the scoped reader classes are what a module developer should create. But not all configuration files have these scoped readers.

Is there a clear path for Magento 2 module developers to follow? Or is this just something Magento 2 module developers should approach in their own way, and the resulting chaos/non-standard-configuration-loading is just the cost of doing business?

The official documentation does a good job of covering some of the available classes, but nothing that reconciles the fact there's no clear guidance on which concrete implementation we're suppose to use, or if the expectation is every module decides how to do this on its own.

Best Answer

To create new configuration type, module developer should create a configuration type class that will be used by clients of configuration.

To make these type classes as simple as possible, all behavior of reading configuration files and caching data was moved to \Magento\Framework\Config\DataInterface with two reusable implementations:

  • \Magento\Framework\Config\Data - for configuration types that only make sense to be loaded in one scope (eav_attributes.xml only in global)
  • \Magento\Framework\Config\Data\Scoped - for configuration types that can be loaded on different scopes (events.xml - global and per-area)

Every configuration type is supposed to have pre-configured Config\DataInterface object. Configuration can be done either with Virtual Type or with inheritance.

Although module developer can technically inherit their configuration type from Config\DataInterface implementation, it's recommended not to extend from core classes. Always better to use composition.

Now \Magento\Framework\Config\Data and Data\Scoped only do caching and delegate configuration reading to \Magento\Framework\Config\ReaderInterface. ReaderInterface is supposed to provide valid configuration in format of PHP array for requested scope (if configuration is scoped). Multiple implementations of ReaderInterface are possible (for example read configuration from DB) but Magento only ships one generic reader: \Magento\Framework\Config\Reader\Filesystem.

\Magento\Framework\Config\Reader\Filesystem does all operations required for reading files from modular filesystem: read files, merge and validate.

Every Config\DataInterface is supposed to have separately configured instance of Config\ReaderInterface. As any instance in system, specific reader can be configured either with Virtual Type or with inheritance. Magento Documentation Describes all Filesystem dependencies.

Every element in this chain is optional (except for Config Type Class itself) and can be substituted with more specific implementation.

Related Topic