System Configuration
Master creating and managing system configuration options that allow store administrators to customize module behavior through the admin panel.
System Configuration Structure
System Configuration Overview
System configuration options allow administrators to customize module behavior through the admin panel at Stores → Configuration.
etc/adminhtml/system.xml
Configuration File Location
Example: Magento/Catalog/etc/adminhtml/system.xml
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>
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>
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>
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>
\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>
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>
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
);
Example:
catalog/recently_products/synchronize_with_backend
- Section:
catalog - Group:
recently_products - Field:
synchronize_with_backend
Scope Parameters
The getValue() method accepts three parameters:
- $path: The configuration path (section/group/field)
- $scopeType: Which type of scope (
\Magento\Store\Model\ScopeInterface)ScopeInterface::SCOPE_WEBSITEScopeInterface::SCOPE_STORE(store view)- Default (if omitted): global scope
- $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
);
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
}
isSetFlag() for Yes/No fields for cleaner code and proper boolean handling.
Complete Example
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
ArrayInterfacefor 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, usegetValue()orisSetFlag() - 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