Validate Your Magento Module’s System Configuration
by David Joly | Wednesday, February 9th, 2011If you are in the business of making Magento extensions you have likely dabbled in Magento’s system.xml files. Let’s face it, if your extension is going to have any flexibility, it’s an absolute must to provide administrators with configuration options. And if you are providing configuration options, you know to validate and filter that data. Wait, Magento lets you validate your system configuration values? Well, yes and no.
Introducing Magento’s Backend Models
Assuming you already have experience with Magento’s system.xml, you will likely have noticed Magento’s references to backend models. Magento has a host of backend models, found in the adminhtml module’s Model/System/Config/Backend directory. These backend models extend Mage_Core_Model_Config_Data. Let’s take a look at Encrypted.php and see just what the heck it does.
/** * Encrypted config field backend model * * @category Mage * @package Mage_Adminhtml */ class Mage_Adminhtml_Model_System_Config_Backend_Encrypted extends Mage_Core_Model_Config_Data { /** * Decrypt value after loading * */ protected function _afterLoad() { $value = (string)$this->getValue(); if (!empty($value) && ($decrypted = Mage::helper('core')->decrypt($value))) { $this->setValue($decrypted); } } /** * Encrypt value before saving * */ protected function _beforeSave() { $value = (string)$this->getValue(); // don't change value, if an obscured value came if (preg_match('/^\*+$/', $this->getValue())) { $value = $this->getOldValue(); } if (!empty($value) && ($encrypted = Mage::helper('core')->encrypt($value))) { $this->setValue($encrypted); } } /** * Get & decrypt old value from configuration * * @return string */ public function getOldValue() { return Mage::helper('core')->decrypt(parent::getOldValue()); } }
Well, that was brief. Magento’s Mage_Core_Model_Config_Data is like other Magento simple models. It extends Mage_Core_Model_Abstract and is mapped to the core_config_data table:
class Mage_Core_Model_Config_Data extends Mage_Core_Model_Abstract { const ENTITY = 'core_config_data'; /** * Prefix of model events names * * @var string */ protected $_eventPrefix = 'core_config_data'; /** * Parameter name in event * * In observe method you can use $observer->getEvent()->getObject() in this case * * @var string */ protected $_eventObject = 'config_data'; /** * Varien model constructor */ protected function _construct() { $this->_init('core/config_data'); } /** * Add availability call after load as public */ public function afterLoad() { $this->_afterLoad(); } /** * Check if config data value was changed * * @return bool */ public function isValueChanged() { return $this->getValue() != $this->getOldValue(); } /** * Get old value from existing config * * @return string */ public function getOldValue() { $storeCode = $this->getStoreCode(); $websiteCode = $this->getWebsiteCode(); $path = $this->getPath(); if ($storeCode) { return Mage::app()->getStore($storeCode)->getConfig($path); } if ($websiteCode) { return Mage::app()->getWebsite($websiteCode)->getConfig($path); } return (string) Mage::getConfig()->getNode('default/' . $path); } /** * Get value by key for new user data from <section>/groups/<group>/fields/<field> * * @return string */ public function getFieldsetDataValue($key) { $data = $this->_getData('fieldset_data'); return (is_array($data) && isset($data[$key])) ? $data[$key] : null; } }
Backend model classes have access to the standard Magento event hooks, _beforeSave(), _afterLoad(), etc. Additionally, as you can see from the code above, they have helper methods getValue(), getOldValue(), isValueChanged(), and getFieldsetDataValue(). These methods, can be used within the event hooks to both validate and filter user input. For example, you can check if the current value is changed:
if($this->isValueChanged()){ /* Do Something */ }
And, if it doesn’t contain an expected value,
if(!is_numeric($this->getValue()){/* Do Something */ }
display an error message and keep the old, still-valid value:
if(!is_numeric($this->getValue()){ Mage::getSingleton('admin/session')->addError('Value must be numeric!'); $this->setValue($this->getOldValue()); }
For the whole picture, the following class test a value to see if it’s numeric and adds an error if it is not.
Zeletron_Module_Model_System_Config_Backend_Numeric extends Mage_Core_Model_Config_Data { protected function _beforeSave() { if(!is_numeric($this->getValue()){ Mage::getSingleton('admin/session')->addError('Value must be numeric!'); $this->setValue($this->getOldValue()); } } }
In theory, you could reference this backend model for all configuration fieldsets that will expect a numeric value. In practice, however, each field should have a separate model to provide a field-specific error message. Either that, or equivalent logic built in to the model class.
Well, that’s it. Short and sweet. And best of all, too easy not to forget to validate.