Section, Group, Field Hierarchy
system.xml follows a three-level hierarchy: Section → Group → Field.
Complete section example:
<?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="vendor_shipping" type="text" sortOrder="300"
showInDefault="1" showInWebsite="1" showInStore="0">
<label>Vendor Shipping</label>
<tab>sales</tab>
<group id="general" type="text" sortOrder="10"
showInDefault="1" showInWebsite="1" showInStore="0">
<label>General Settings</label>
<field id="enabled" type="select" sortOrder="10"
showInDefault="1" showInWebsite="1" showInStore="0">
<label>Enable Module</label>
<source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
</field>
</group>
<group id="rates" type="text" sortOrder="20"
showInDefault="1" showInWebsite="0" showInStore="0">
<label>Shipping Rates</label>
<field id="flat_rate" type="price" sortOrder="10"
showInDefault="1" showInWebsite="0" showInStore="0">
<label>Flat Rate Fee</label>
<backend_model>Magento\Config\Model\Config\Backend\Price</backend_model>
</field>
</group>
</section>
</system>
</config>
Hierarchy levels:
Section (id="vendor_shipping")
├── Group (id="general")
│ ├── Field (id="enabled")
│ └── Field (id="title")
└── Group (id="rates")
├── Field (id="flat_rate")
└── Field (id="free_threshold")
Field Types
system.xml supports various field types for different input needs.
Text field:
<field id="api_key" type="text" sortOrder="10" showInDefault="1">
<label>API Key</label>
<validate>required-entry</validate>
</field>
Boolean (Yes/No):
<field id="enabled" type="select" sortOrder="10" showInDefault="1">
<label>Enable Module</label>
<source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
</field>
Select (dropdown):
<field id="shipping_method" type="select" sortOrder="20" showInDefault="1">
<label>Default Method</label>
<source_model>Vendor\Module\Model\Config\Source\ShippingMethods</source_model>
</field>
Multiselect:
<field id="allowed_countries" type="multiselect" sortOrder="30" showInDefault="1">
<label>Allowed Countries</label>
<source_model>Magento\Directory\Model\Config\Source\Country</source_model>
<backend_model>Magento\Config\Model\Config\Backend\Serialized\ArraySerialized</backend_model>
</field>
Textarea:
<field id="custom_css" type="textarea" sortOrder="40" showInDefault="1">
<label>Custom CSS</label>
<backend_model>Magento\Config\Model\Config\Backend\Textarea</backend_model>
</field>
Image upload:
<field id="logo_image" type="image" sortOrder="50" showInDefault="1">
<label>Logo Image</label>
<backend_model>Magento\Config\Model\Config\Backend\Image</backend_model>
<upload_dir>vendor/logo</upload_dir>
</field>
Obscure (password-like):
<field id="secret_key" type="obscure" sortOrder="60" showInDefault="1">
<label>Secret Key</label>
<backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model>
</field>
Price:
<field id="handling_fee" type="price" sortOrder="70" showInDefault="1">
<label>Handling Fee</label>
<backend_model>Magento\Config\Model\Config\Backend\Price</backend_model>
</field>
Backend Models
Backend models handle save logic, validation, and data processing for configuration values.
Custom backend model:
<?php
namespace Vendor\Module\Model\Config\Backend;
class ApiKey extends \Magento\Config\Model\Config\Backend\AbstractBackend
{
public function afterSave(): void
{
$value = $this->getValue();
// Validate API key format
if (!preg_match('/^[A-Za-z0-9]{32}$/', $value)) {
throw new \Magento\Framework\Exception\LocalizedException(
__('API Key must be 32 alphanumeric characters')
);
}
// Process after save
$this->cacheClean();
}
public function beforeSave(): void
{
$value = $this->getValue();
// Trim whitespace
$this->setValue(trim($value));
}
private function cacheClean(): void
{
// Clear relevant cache
}
}
Backend model in system.xml:
<field id="api_key" type="obscure" sortOrder="20" showInDefault="1">
<label>API Key</label>
<backend_model>Vendor\Module\Model\Config\Backend\ApiKey</backend_model>
</field>
Built-in backend models:
| Backend Model | Purpose |
|---|---|
| Magento\Config\Model\Config\Backend\Price | Price validation |
| Magento\Config\Model\Config\Backend\Image | Image upload handling |
| Magento\Config\Model\Config\Backend\Textarea | Textarea processing |
| Magento\Config\Model\Config\Backend\Encrypted | Encryption for sensitive data |
| Magento\Config\Model\Config\Backend\Serialized\ArraySerialized | Serialized array storage |
| Magento\Config\Model\Config\Backend\Note | Display note/message |
Backend model lifecycle:
1. User submits config form
2. beforeSave() called
3. Value validated
4. afterSave() called
5. Value stored in core_config_data
Source Models and Custom Options
Source models provide options for select and multiselect fields.
Custom source model:
<?php
namespace Vendor\Module\Model\Config\Source;
class ShippingMethods implements \Magento\Framework\Option\ArrayInterface
{
public function toOptionArray(): array
{
return [
['value' => 'flat', 'label' => __('Flat Rate')],
['value' => 'free', 'label' => __('Free Shipping')],
['value' => 'table', 'label' => __('Table Rates')],
];
}
}
Source model in system.xml:
<field id="shipping_method" type="select" sortOrder="20" showInDefault="1">
<label>Default Method</label>
<source_model>Vendor\Module\Model\Config\Source\ShippingMethods</source_model>
</field>
Dynamic source model (from database):
<?php
namespace Vendor\Module\Model\Config\Source;
class Countries implements \Magento\Framework\Option\ArrayInterface
{
public function __construct(
private \Magento\Directory\Model\ResourceModel\Country\CollectionFactory $countryCollection
) {}
public function toOptionArray(): array
{
$countries = $this->countryCollection->create()->loadByStore();
$options = [];
foreach ($countries as $country) {
$options[] = [
'value' => $country->getId(),
'label' => $country->getName()
];
}
return $options;
}
}
Built-in source models:
| Source Model | Options |
|---|---|
| Magento\Config\Model\Config\Source\Yesno | Yes/No |
| Magento\Config\Model\Config\Source\Enabledisable | Enable/Disable |
| Magento\Config\Model\Config\Source\Locale | All locales |
| Magento\Config\Model\Config\Source\Store | All stores |
| Magento\Directory\Model\Config\Source\Country | All countries |
Validation rules:
<!-- required-entry: Must have value -->
<validate>required-entry</validate>
<!-- validate-email: Must be valid email -->
<validate>validate-email</validate>
<!-- validate-url: Must be valid URL -->
<validate>validate-url</validate>
<!-- validate-number: Must be numeric -->
<validate>validate-number</validate>
<!-- Multiple validations -->
<validate>required-entry validate-number validate-digits</validate>
Quiz
1. What field type is used for Yes/No options?
2. What method does a backend model call after saving a value?
3. Which field type should be used for image uploads?
4. What interface do source models implement?
Flashcards
Question
What are the three levels of system configuration?
Click to reveal answer
Answer
Section → Group → Field
Question
What field type handles Yes/No selections?
Click to reveal answer
Answer
select with Magento\Config\Model\Config\Source\Yesno
Question
What is a backend model responsible for?
Click to reveal answer
Answer
Save logic, validation, and data processing for config values
Question
What validation rule makes a field required?
Click to reveal answer
Answer
required-entry
Question
Where is system.xml located in a module?
Click to reveal answer
Answer
Vendor/Module/etc/adminhtml/system.xml
Revision Notes
Key Takeaways
- 1. system.xml follows Section → Group → Field hierarchy
- 2. Field types: text, select, multiselect, textarea, image, price, obscure
- 3. Source models provide dropdown options via toOptionArray()
- 4. Backend models handle beforeSave/afterSave logic
- 5. Visibility controlled by showInDefault/showInWebsite/showInStore
- 6. Validate attribute supports multiple validation rules
Interview Tips
- • Explain the section/group/field hierarchy
- • Describe how to create custom source and backend models
- • Know the difference between all field types
- • Discuss scope configuration (default, website, store)
Cheat Sheet
system.xml Cheat Sheet
Field types:
- text: Text input
- select: Dropdown (with source_model)
- multiselect: Multiple selections
- textarea: Multi-line text
- image: Image upload
- price: Price with validation
- obscure: Masked input (passwords)
Source model:
class MySource implements ArrayInterface
{
public function toOptionArray(): array { ... }
}
Backend model:
class MyBackend extends AbstractBackend
{
public function afterSave(): void { ... }
}
Visibility:
showInDefault="1" showInWebsite="1" showInStore="0"
Validation: