Create Custom System Configuration Fields in Magento

adminformconfigurationformssystem.xml

I'd like to create my custom field type to use it in system config forms (System -> Configuration). For example, to create simple text field we need to use <frontend_type>text</frontend_type> in system.xml which tells Magento to use file lib/Varien/Data/Form/Element/Text.php:

<config>
    <!-- ... -->
    <sections>
        <mycustom_section translate="label">
            <label>My Module</label>
            <sort_order>0</sort_order>
            <show_in_default>1</show_in_default>
            <show_in_website>1</show_in_website>
            <show_in_store>1</show_in_store>
            <groups>
                <mycustom_group translate="label">
                    <label>My Custom Group</label>
                    <sort_order>0</sort_order>
                    <show_in_default>1</show_in_default>
                    <show_in_website>1</show_in_website>
                    <show_in_store>1</show_in_store>
                    <fields>
                        <heading_sample translate="label">
                            <label>Sample heading</label>
                            <sort_order>0</sort_order>
                            <show_in_default>1</show_in_default>
                            <show_in_website>1</show_in_website>
                            <show_in_store>1</show_in_store>
                            <frontend_model>adminhtml/system_config_form_field_heading</frontend_model>
                        </heading_sample>
                        <mycustom_field translate="label comment">
                            <label>My Custom Field</label>
                            <sort_order>1</sort_order>
                            <show_in_default>1</show_in_default>
                            <show_in_website>1</show_in_website>
                            <show_in_store>1</show_in_store>
                            <frontend_type>text</frontend_type> <!-- custom field type here -->
                            <frontend_model>mymodule/custom</frontend_model> <!-- or custom model here -->
                        </mycustom_field>
                        <!-- ...other fields here... -->
                    </fields>
                </mycustom_group>
            </groups>
        </mycustom_section>
    </sections>
</config>

If I create my own class (that extends Magento's Varien_Data_Form_Element_Abstract class) that renders my custom field, where I should put this file?

It will probably work (will it?) if I put it inside Magento default directory
lib/Varien/Data/Form/Element/Customized.php:

<mycustom_group>
<!-- ... -->
    <fields>
        <mycustom_field translate="label comment">
            <label>My Custom Field</label>
            <comment>Some comment about my field</comment>
            <show_in_default>1</show_in_default>
            <show_in_website>1</show_in_website>
            <show_in_store>1</show_in_store>
            <frontend_type>customized</frontend_type>  <!-- custom field type here -->
        </mycustom_field>
    </fields>
</mycustom_group>
  1. But is there any way to keep my custom field file inside my custom module instead in lib/Varien/Data/Form/Element/? Adding custom files to the core directories isn't probably very good idea.

  2. If not, can I create custom folder inside lib/, for example: lib/Mycompany/Customized.php? But in that case, how I can inform Magento in the <frontend_type> tag inside system.xml that it should use my custom field file and tell Magento where that file is located?

Best Answer

So the easy but not recommended way is to put your element under app/code/local/Varien/Data/Form/Element/. As Magento is simply using the order of include_paths and will first look into the local folder, before it goes to the lib folder.

But the wiring to Varien_Data_Form_Element is just a fallback which is defined in lib\Varien\Data\Form\Abstract.php.

public function addField($elementId, $type, $config, $after=false)
{
    if (isset($this->_types[$type])) {
        $className = $this->_types[$type];
    }
    else {
        $className = 'Varien_Data_Form_Element_'.ucfirst(strtolower($type));
    }

So the question is where can you defined that _types array? There is an addType method in the same class which can be called. When you search for that method you might stumble over an iteration of _getAdditionalElementTypes on the form block in app\code\core\Mage\Adminhtml\Block\Widget\Form.php

protected function _addElementTypes(Varien_Data_Form_Abstract $baseElement)
{
    $types = $this->_getAdditionalElementTypes();
    foreach ($types as $code => $className) {
        $baseElement->addType($code, $className);
    }
}

The default implementation of _getAdditionalElementTypes just returns an empty array, but there is already some working implementation in the Magento Core in Mage_Adminhtml_Block_Sales_Order_Create_Form_Abstract.

So that example looks like:

protected function _getAdditionalFormElementTypes()
{
    return array(
        'file'      => Mage::getConfig()->getBlockClassName('adminhtml/customer_form_element_file'),
        'image'     => Mage::getConfig()->getBlockClassName('adminhtml/customer_form_element_image'),
        'boolean'   => Mage::getConfig()->getBlockClassName('adminhtml/customer_form_element_boolean'),
    );
}

And even in your form which you need to extend: Mage_Adminhtml_Block_System_Config_Form

protected function _getAdditionalElementTypes()
{
    return array(
        'export'        => Mage::getConfig()->getBlockClassName('adminhtml/system_config_form_field_export'),
        'import'        => Mage::getConfig()->getBlockClassName('adminhtml/system_config_form_field_import'),
        'allowspecific' => Mage::getConfig()
            ->getBlockClassName('adminhtml/system_config_form_field_select_allowspecific'),
        'image'         => Mage::getConfig()->getBlockClassName('adminhtml/system_config_form_field_image'),
        'file'          => Mage::getConfig()->getBlockClassName('adminhtml/system_config_form_field_file')
    );
}

Based on that you should be able to implement your own _getAdditonalFormElementTypes by rewriting the System_Config_Form which can then handle new frontend types in your config.

€: Thinking about the question of Fabian it should also be possible by using an observer. The addType method on the form element is public, so you can call that and add custom types from and observer. You need to listen to an event in the form object and in the observer add your custom types. Just make sure this happens before _initFields, which is called from initForm. So a good point can be the prepare_layout events which are called before the initForm method (see Mage_Adminhtml_Block_System_Config_Edit->initForm and Mage_Core_Block_Abstract->setLayout)