Magento2 configuration tweaks

Magento2 configuration tweaks

June 14, 2020

Introduction #

Magento2 (as well as Magento 1) implements really good interface to handle configurations for 3rd party modules. Developers are able to create rich and informative configuration panels. However, sometimes it feels like extensions use only basic features provided by Magento. No surprise, some of them are hidden or not documented well enough.

This article tries to put some lights on not-so-well-known features that you can use to make your confiiguration panel better. Enjoy!

Using extends #

Extends system allows to use some-kind-of-inheritance between fields and groups.

Let’s say we want to store contact data of 3 people, something like this:

alt tex2t

So three contacts which have same set of inputs:

  • firstname,
  • lastname,
  • e-mail address

With normal approach we would create 3 groups with same fields inside (3 x 3 fields). Extend attribute allows us to it smarter, using some kind of inherance feature, so we can create “virtual” group for contact, and then reuse it for our contact groups. Take a look on following snippets to get better understanding:

<group id="contact">
    <label>Contact data</label>
    <field id="firstname" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1">
        <label>Firstname</label>
    </field>
    <field id="lastname" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1">
        <label>Lastname</label>
    </field>
    <field id="email" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1">
        <label>E-mail address</label>
    </field>
</group>

<group id="contact_erp_manager" extends="contact" translate="label" type="text" sortOrder="0" showInDefault="1" showInWebsite="1" showInStore="1" >
    <label>ERP manager</label>
</group>
<group id="contact_erp_support"  translate="label" type="text" sortOrder="0" showInDefault="1" showInWebsite="1" showInStore="1">
    <label>Support contact</label>
</group>
<group id="contact_erp_it" extends="contact" translate="label" type="text" sortOrder="0" showInDefault="1" showInWebsite="1" showInStore="1" extends="contact">
    <label>ERP IT contact</label>
</group>
Please notice that group with id contact has no attributes like showInDefault, showInStore - this way it will not be shown as separate group, only used for inheritance.

💡 Things to consider:

  • You can extend groups or whole sections
  • You can extend groups from other sections, for this you need to specify full path, f.e: newsletter/general.
  • Currently, only PayPal extensions uses it, I couldn’t find any information about it online - had to xdebug whole thing

Using subgroups #

Most of people are aware, that group nodes can contain group - for example PayPal extension, due to its complex nature uses it heavily:

alt tex2t

However - not so many knows that there is no limitation - so we have option to create subgroups inception and go deeper and deeper:

alt tex2t

What canRestore does? #

In field declaration, you can specify attribute canRestore and set it to 0 or 1 - as result, field will be rendered with additional checkbox, where admin user will be able to revert field’s value to it’s initial value (taken from config.xml)

alt tex2t

Using tooltips, comments, hints #

Magento admin panels allows us to make our panel more user friendly with suggestions and explanations to people who will use our extensions. We have 3 basic ways to explain meaning of specific fields:

Comment #

Most basic one, content will be rendered below field, we are allowed to use basic HTML (don’t forget about ). What is cool and is not documented at all is that you are not limited to literal strings, but you can deliver content for comment through block. To make it happen you must define classname using this syntax:

<field id="email" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1">
  <label>E-mail address</label>
  <comment>
    <model>Namespace\Module\Block\Adminhtml\Form\Field\Comment</model>
  </comment>
</field>

You must create respective block which implements \Magento\Config\Model\Config\CommentInterface - for example:

<?php

namespace Namespace\Module\Block\Adminhtml\Form\Field;

class Comment implements \Magento\Config\Model\Config\CommentInterface
{
    public function getCommentText($elementValue)
    {
        return 'Hello!';
    }
}

With this, you are able to put some logic into comment context - $elementValue contains current value of field.

Tooltips #

Tooltips can be added simply by providing node. When tooltip is added, it will be shown as help icon next to the field, when on hover, content of tooltip will be shown.

alt tex2t

Similar to comments, tooltips can also have their own classname provided to get content from custom block:

<field id="lastname" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="0">
   <label>Lastname</label>
   <tooltip_block>Namespace\Module\Block\Adminhtml\Form\Field</tooltip_block>
</field> 
Please note, that syntax is different than in comments
<?php

namespace Creativestyle\CustomizationLensplaza\Block\Adminhtml\Form\Field;

class Tooltip extends \Magento\Framework\View\Element\Template
{
    public function _toHtml()
    {
        return 'I am a tooltip content!';
    }
}
Tooltips should be used for fields which might be confusing or hard to explain - tooltips give you more space to explain specifics of the field. However, it should contain additional explanation, not essential one.

Hints #

Last group are hints - I mention this, but somehow it is broken. When you add to your field - it will be rendered in invisible div with class “hint”, hovering on this element would show hint’s content, however - this div is not styled and cannot receive any focus making whole concept unusable.

Using requires #

Sometimes fields depend on each other and we need have some data filled beforehand. This is where requires comes to help. Take a look at following example:

alt tex2t

In this case, option to notify contact is enabled only when all three fields are filled up (which makes sense). Syntax for this scenario is:
<field id="active" translate="label" type="select" sortOrder="100" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1">
   <label>Notify this contact</label>
   <source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
   <requires>
       <field id="email" />
       <field id="firstname" />
       <field id="lastname" />
   </requires>
</field> 
You can also use group_id, in this case all fields in this group must be filled.

Unfortunately, it enables field in situation where are required fields are not empty - it does not run validations, so even if my email field has set validate-email, it is not checked against requirements - so providing anything in e-mail field is enough to enable dropdown. This feature should be used to set order of inputted fields, for example - it should not be possible to enable 3rd party integration, if access data is not inputted.

Using includes #

When project gets bigger and new features are getting their configuration settings, system.xml file can get really big and lose clarity. It is recommended to use specific file for each feature or piece of logic using <include> directive:

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="module_settings" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1" showInStore="1">
            <label>YourModule - Settings</label>
            <resource>Namespace_Module::config_settings</resource>
            <include path="Namespace_Module::system/group_1.xml"/>
            <include path="Namespace_Module::system/group_2.xml"/>
        </section>
    </system>
</config>

Please note, there are two <include> nodes which point to xml file where definition of group is stored. For example - system/group_1.xml:

<?xml version="1.0" encoding="UTF-8"?>
<include xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_include.xsd">
    <group id="email_settings" translate="label" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1">
        <label>Some settings</label>
        <field id="email" translate="label" sortOrder="250" showInDefault="1" showInWebsite="1" showInStore="1">
            <label>Email address</label>
        </field>
        ...
    </group>
</include>

This will make everything cleaner and it will also help to avoid conflicts on system.xml file, since everyone will have his/her own file to work on.

Passing data from xml to custom frontend model #

Let’s assume, you have created custom frontend model for your field and you want to be able to provide some data into it through configuration.

Something like this will not work: (it used to work this way in Magento 1.x)

<group id="deliverytime" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1">
    <label>Custom group</label>        
    <frontend_model>Creativestyle\...\Class</frontend_model>
    <url>https://customurl.com</url>
    <width>100</width>
    <height>100</height>      
</group>

In your class, you will not have access to url, width and height, because it will be stripped out. What you can do instead, is to define it this way:

Something like this will work:

<group id="deliverytime" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1">
    <label>Custom group</label>        
    <frontend_model>Creativestyle\...\Class</frontend_model>
    <attribute type="url">https://customurl.com</attribute>
    <attribute type="width">100</attribute>
    <attribute type="height">100</attribute>   
</group>

Now, in your class, your able to fetch this data using $element->getGroup()- you will receive array of all fields defined in XML, including attributes. If you are doing this for field renderer, you need to use: $element->getOriginalData()

Using dependencies #

Depend field is well known and commonly used - it allows conditional display of selected fields based on value(s) of other fields - making configuration panel interactive and more user friendly. There are still some nice, less knows features:

There can be more than one dependency: #

<depends>
    <field id="*/*/active">1</field>
    <field id="*/*/item_variant_layer">1</field>
</depends>

In this case, field will be shown when both conditions are true. There is no limit in number of dependant fields.

Select and multiselect can be handled #

for example if you want to show field in case if dropdown’s values are 3,4,5 - you need to provide separator attribute:

<depends>
    <field id="*/*/enabled">1</field>
    <field id="*/*/option" separator=",">3,4,5</field>
</depends>

It is possible to use negative attribute - to negate condition, for example: #

<depends>
    <field id="protection_type" separator="," negative="1">0</field>
</depends>

In this case, field be shown when protection_type will be different than 0 (not equal).

Other useful things #

Checking for module’s status #

You can render some specific field only, when other module is enabled, for this you can useif_module_enabled - this will check for module’s status and depending on that - it will render field or not:

<if_module_enabled>Magento_PageBuilder</if_module_enabled>

Hiding in single store mode #

If your feature makes sense only for multistore, then it might be useful to hide field for systems with just one store, for this, you can use:

<hide_in_single_store_mode>1</hide_in_single_store_mode>

Define custom config path #

I have no idea what this can be used for, but by providing own <custom_path> node you change path, so it not taken from section/group/field params, but your own:

<field id="email" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="0">
  <label>E-mail address</label>
  <config_path>who/Needs/It</config_path>
</field>

Final words #

I hope you found something new or interesting for you. In future I will add more articles about extending configuration panels with custom frontend and backend models, improved

🍺 If you liked this article you might consider buying me a beer? ;)