2.03

System Configuration

Master creating and managing system configuration options that allow store administrators to customize module behavior through the admin panel.

Why This Matters: System configuration allows store administrators to customize module behavior without touching code. Understanding how to create configuration options is essential for building flexible, configurable Magento modules.

System Configuration Structure

mindmap root((system.xml)) Structure Tab Section Group Field File Location etc/adminhtml/system.xml Field Types text select multiselect obscure allowspecific Access Values ScopeConfigInterface getValue method Scope aware

System Configuration Overview

System configuration options allow administrators to customize module behavior through the admin panel at Stores → Configuration.

File Location: System configuration options are configured in etc/adminhtml/system.xml

Configuration File Location

etc/adminhtml/system.xml

Example: Magento/Catalog/etc/adminhtml/system.xml

⚠️ Important: Since these are XML files, you must clear the cache for Magento to recognize changes. Bear in mind that when you clear the cache, you will experience a longer-than-normal load time. This can be a problem in production.

Configuration Structure Hierarchy

The system configuration follows a hierarchical structure:

<config>
    <system>
        <tab>           <!-- Top-level tab (e.g., "Sales", "Catalog") -->
        <section>       <!-- Section within a tab (e.g., "Inventory") -->
            <group>     <!-- Group of related fields -->
                <field> <!-- Individual configuration field -->
                </field>
            </group>
        </section>
    </system>
</config>

Creating a Tab

Tabs appear at the top level of the configuration page (e.g., General, Catalog, Sales, etc.).

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd">
    <system>
        <tab id="catalog" translate="label" sortOrder="200">
            <label>Catalog</label>
        </tab>
    </system>
</config>
Best Practice: If you work for a module vendor or agency, put all configuration choices in a pertinent area in Store Configuration. Do NOT put it under a tab for your company. For example, if you create an Order Attribute module, put configuration under Sales → Order Attributes and NOT [MODULE VENDOR] → Order Attributes. It makes administration much easier.

Creating a Section

The section tag lives inside the system tag (not a tab element). The section appears under the tab headings.

<system>
    <section id="catalog" translate="label" type="text" sortOrder="40" 
             showInDefault="1" showInWebsite="1" showInStore="1">
        <class>separator-top</class>
        <label>Catalog</label>
        <tab>catalog</tab>
        <resource>Magento_Catalog::config_catalog</resource>
        
        <!-- Groups go here -->
    </section>
</system>

Section Attributes

  • id: Unique identifier for the section
  • translate: Which attributes to translate (usually "label")
  • sortOrder: Display order within the tab
  • showInDefault: Show in global scope (1 = yes, 0 = no)
  • showInWebsite: Show in website scope
  • showInStore: Show in store view scope

Section Elements

  • <class>: CSS class (e.g., "separator-top" adds a separator)
  • <label>: Display name of the section
  • <tab>: Which tab this section belongs to
  • <resource>: ACL resource required to access this section

Creating a Group

Groups reside inside the section tag and group related configuration fields together.

<section id="catalog">
    <group id="recently_products" translate="label" type="text" sortOrder="350" 
           showInDefault="1" showInWebsite="1" showInStore="1">
        <label>Recently Viewed/Compared Products</label>
        
        <!-- Fields go here -->
    </group>
</section>

Nested Groups

You can also nest groups for more complex configurations. This is seen on the Stores → Configuration → Sales → Payment Methods → Authorize.net page.

Example: Magento/AuthorizenetAcceptjs/etc/adminhtml/system.xml

When nesting groups, use the config_path value for a field to configure where this value is stored:

<field id="title">
    <label>Title</label>
    <config_path>payment/authorizenet_acceptjs/title</config_path>
</field>

Creating a Field (Entry)

Fields are the individual configuration inputs. The field tag is found inside a group element.

<group id="recently_products">
    <field id="synchronize_with_backend" translate="label" type="select" 
           sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1">
        <label>Synchronize widget products with backend storage</label>
        <source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
    </field>
</group>
Scope Attributes: This example uses the showInDefault attribute, which means the field is visible in the global scope. You can also use showInWebsite or showInStore (store view scope).

Field Types

The type attribute determines what type of input is used for a given option.

Common Field Types

text

Shows a single line text field. Ideally used for values that a store administrator would want to control.

<field id="title" type="text">
    <label>Title</label>
</field>
Use Case: Text that needs translation across scopes

select

Shows a dropdown list. Values are specified in a source_model element.

<field id="enabled" type="select">
    <label>Enabled</label>
    <source_model>
        Magento\Config\Model\Config\Source\Yesno
    </source_model>
</field>
Source Model: Must implement \Magento\Framework\Option\ArrayInterface

multiselect

Shows a multiple-select list allowing selection of multiple values (e.g., countries).

<field id="countries" type="multiselect">
    <label>Allowed Countries</label>
    <source_model>
        Magento\Directory\Model\Config\Source\Country
    </source_model>
</field>

obscure

Presents a password input with encryption. Include a backend_model element.

<field id="api_key" type="obscure">
    <label>API Key</label>
    <backend_model>
        Magento\Config\Model\Config\Backend\Encrypted
    </backend_model>
</field>
Note: There is a password type, but the value is not encrypted. Use obscure for sensitive data.

allowspecific

Powers the "Ship to Applicable Countries" functionality. Controls whether a countries select field is enabled.

<field id="sallowspecific" type="allowspecific">
    <label>Ship to Applicable Countries</label>
    <source_model>Magento\Config\Block\System\Config\Form\Field\Select\Allowspecific</source_model>
</field>

Field Configuration Elements

While you may not use these every day, they will come in handy from time to time:

Element/Attribute Type Purpose Example
config_path Element Map a field to a different configuration path. Useful when moving settings without breaking references. <config_path>old/path/value</config_path>
label Element The display name of this field <label>Enable Module</label>
sortOrder Attribute Configures the order in which elements appear sortOrder="10"
depends Element Makes the current field dependent on another field's value <depends><field id="enabled">1</field></depends>
validate Element Apply validation rules. Multiple classes can be space-separated. <validate>required-entry</validate>
backend_model Element Class that inherits \Magento\Framework\App\Config\Value for before/after save/delete functionality <backend_model>Vendor\Module\Model\Config\Backend\Custom</backend_model>
frontend_model Element Block that renders the element. Extend classes in \Magento\Config\Block\System\Config\Form namespace <frontend_model>Vendor\Module\Block\Config\Custom</frontend_model>

Examples of Field Configuration

Required Field with Validation

<field id="email" type="text">
    <label>Email Address</label>
    <validate>required-entry validate-email</validate>
</field>

Field with Dependency

<field id="enabled" type="select">
    <label>Enable Feature</label>
    <source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
</field>

<field id="api_key" type="text">
    <label>API Key</label>
    <depends>
        <field id="enabled">1</field>
    </depends>
</field>
Result: The API Key field only appears when "Enable Feature" is set to "Yes"

Accessing Configuration Values

To access configuration values in your code, follow these steps:

Step 1: Inject ScopeConfigInterface

<?php
namespace Vendor\Module\Model;

use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Store\Model\ScopeInterface;

class ConfigHelper
{
    protected $scopeConfig;
    
    public function __construct(ScopeConfigInterface $scopeConfig)
    {
        $this->scopeConfig = $scopeConfig;
    }
}

Step 2: Use getValue() Method

<?php
// Basic usage (global scope)
$value = $this->scopeConfig->getValue('catalog/recently_products/synchronize_with_backend');

// Website scope
$value = $this->scopeConfig->getValue(
    'catalog/recently_products/synchronize_with_backend',
    ScopeInterface::SCOPE_WEBSITE,
    $websiteId
);

// Store view scope
$value = $this->scopeConfig->getValue(
    'catalog/recently_products/synchronize_with_backend',
    ScopeInterface::SCOPE_STORE,
    $storeId
);
Configuration Path: The scope configuration path is realized by concatenating the section id, the group id, and the field id separated by slashes.

Example: catalog/recently_products/synchronize_with_backend
  • Section: catalog
  • Group: recently_products
  • Field: synchronize_with_backend

Scope Parameters

The getValue() method accepts three parameters:

  1. $path: The configuration path (section/group/field)
  2. $scopeType: Which type of scope (\Magento\Store\Model\ScopeInterface)
    • ScopeInterface::SCOPE_WEBSITE
    • ScopeInterface::SCOPE_STORE (store view)
    • Default (if omitted): global scope
  3. $scopeCode: The identifier for the scope (website ID or store ID)

Getting Store ID from Order

<?php
// If you need a configuration associated with a particular order
$storeId = $order->getStoreId();

$value = $this->scopeConfig->getValue(
    'section/group/field',
    ScopeInterface::SCOPE_STORE,
    $storeId
);
⚠️ Common Mistake: Not considering the scope and scope ID when loading values from store configuration. This is often not necessary on a frontend template. However, in the admin, this is often important unless the scope for a configuration setting is global.

isSetFlag() for Boolean Values

For boolean configuration values (Yes/No), use isSetFlag() instead of getValue():

<?php
// Returns true if value is "1", false otherwise
$isEnabled = $this->scopeConfig->isSetFlag(
    'catalog/recently_products/synchronize_with_backend',
    ScopeInterface::SCOPE_STORE,
    $storeId
);

if ($isEnabled) {
    // Feature is enabled
}
Best Practice: Use isSetFlag() for Yes/No fields for cleaner code and proper boolean handling.

Complete Example

system.xml

etc/adminhtml/system.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd">
    <system>
        <section id="mymodule" translate="label" sortOrder="300" showInDefault="1" showInWebsite="1" showInStore="1">
            <class>separator-top</class>
            <label>My Module Configuration</label>
            <tab>general</tab>
            <resource>Vendor_Module::config</resource>
            
            <group id="general" translate="label" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1">
                <label>General Settings</label>
                
                <field id="enabled" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1">
                    <label>Enable Module</label>
                    <source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
                </field>
                
                <field id="api_key" translate="label" type="obscure" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="0">
                    <label>API Key</label>
                    <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model>
                    <depends>
                        <field id="enabled">1</field>
                    </depends>
                </field>
                
                <field id="email" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="1">
                    <label>Notification Email</label>
                    <validate>required-entry validate-email</validate>
                    <depends>
                        <field id="enabled">1</field>
                    </depends>
                </field>
            </group>
        </section>
    </system>
</config>

Accessing in Code

<?php
namespace Vendor\Module\Helper;

use Magento\Framework\App\Helper\AbstractHelper;
use Magento\Framework\App\Helper\Context;
use Magento\Store\Model\ScopeInterface;

class Data extends AbstractHelper
{
    public function isEnabled($storeId = null)
    {
        return $this->scopeConfig->isSetFlag(
            'mymodule/general/enabled',
            ScopeInterface::SCOPE_STORE,
            $storeId
        );
    }
    
    public function getApiKey($storeId = null)
    {
        return $this->scopeConfig->getValue(
            'mymodule/general/api_key',
            ScopeInterface::SCOPE_STORE,
            $storeId
        );
    }
    
    public function getNotificationEmail($storeId = null)
    {
        return $this->scopeConfig->getValue(
            'mymodule/general/email',
            ScopeInterface::SCOPE_STORE,
            $storeId
        );
    }
}

Best Practices

Configuration Best Practices

  • Logical Organization: Put configuration in pertinent areas (e.g., Sales → Order Attributes, not Vendor → Order Attributes)
  • Scope Awareness: Always consider scope when retrieving config values, especially in admin context
  • Cache Clearing: Remember to clear cache after modifying system.xml files
  • Use obscure type: For sensitive data like API keys, passwords - encrypts values in database
  • Use isSetFlag(): For boolean values instead of getValue()
  • ACL Resources: Always specify ACL resource in sections to control access
  • Validation: Add appropriate validation rules to prevent invalid input
  • Dependencies: Use depends element to show/hide fields based on other field values
  • Source Models: Implement ArrayInterface for select/multiselect options
  • Translation: Use translate="label" for multilingual support

Exam Tips

Key Points to Remember

  • File location: etc/adminhtml/system.xml
  • Hierarchy: Tab → Section → Group → Field
  • Config path format: section_id/group_id/field_id
  • Access method: Inject ScopeConfigInterface, use getValue() or isSetFlag()
  • Scope types: SCOPE_WEBSITE, SCOPE_STORE (store view), default (global)
  • Field types: text, select, multiselect, obscure, allowspecific
  • obscure vs password: Use obscure for encrypted values, password is plain text
  • Source model: Must implement \Magento\Framework\Option\ArrayInterface
  • Encrypted backend model: Magento\Config\Model\Config\Backend\Encrypted
  • Cache clearing: Required after modifying system.xml files
  • Common mistake: Forgetting scope awareness when loading config values

Further Reading