Magento 2 – Where to Store Persistent Single Variables (Module Settings)

magento2persistentvariables

I've read through the Magento 2 dev docs, and I can't seem to find were we store persistent variables, if at all. I'm trying to store a token value for a custom module.

I've searched a fair amount online, researching saving to the registry, but the registry doesn't save data persistently. I see that Magento 2 has "variable" and "variable_value" tables–is this where they are stored?

I'm looking for something like wp_option in WordPress or variables in Drupal 7, if anyone is familiar.

Or is it the case that we create our own tables to save module settings?

Best Answer

Magento provides a facility to persist configuration values like tokens, passwords, URLs etc. This is the System (Store) Configuration (Menu > Stores > Configuration).

See the /etc section of this documentation on system.xml and other configuration files: http://devdocs.magento.com/guides/v2.1/architecture/archi_perspectives/components/modules/mod_anatomy.html

So, in your module directory etc/adminhtml/system.xml will hold the XML which will create your configuration.

You have an option to create a new section for your configuration or to add your configuration to an already existing area. You can also create a whole new tab, but this is typically not a good idea, as it tends to clutter up the top navigation. You can most-likely find a good spot in the existing configuration for your settings.

There's quite a bit of stuff in there, but the main choices you need to make are the scope (See more on that here: http://docs.magento.com/m2/ee/user_guide/configuration/scope.html) and the type of data you are storing.

Scope means whether or not there is a single global value, or the user can set a different value for different website, store and store view. In your case, you probably want to allow the user to have a different token for a different website, but not store views.

Data types will typically be "text" for your use case, but when stored it may also be encrypted in case you consider this to be something that users should be able to only set, but not read once it has been saved. (You will be able to decrypt the value through code.)

ACL If you are creating a new tab (as in the example below), you should also define an ACL resource, so that you may allow or prohibit access for only some User Roles.

You can find many examples of system.xml, acl.xml and config.xml in the Magento core modules, so you can take a look at those and fashion your own.

Example So, let's say your module integrates with an external service and you consider the token to be secret. Your configuration should probably live in the Services Menu, the scope will be available globally but can be overridden with different values for each website. The data type will be Encrypted.

etc/adminhtml/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="mycompany_mymodule" sortOrder="1000" showInDefault="1" showInWebsite="1" showInStore="0" type="text"> <!-- Create a new section with selected scope -->
            <label>Token Integration</label>
            <tab>service</tab> <!-- The tab we are adding this section to -->
            <resource>Mycompany_Mymodule::system_config</resource> <!-- The ACL Resource -->
            <group id="configuration" translate="label" showInDefault="1" showInWebsite="1" showInStore="0" sortOrder="1">
                <label>Configuration</label>
                <field id="token" type="select" showInDefault="1" showInWebsite="1" showInStore="0" sortOrder="10">
                    <label>Token</label>
                    <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> <!-- This will ensure that the value is encrypted when it is saved and decrypted when pulled out of the database. You will need to add one more bit in order for this decryption to work -->
                </field>
            </group>
        </section>
    </system>
</config>

etc/adminhtml/acl.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Acl/etc/acl.xsd">
    <acl>
        <resources>
            <resource id="Magento_Backend::admin">
                <resource id="Magento_Backend::stores">
                    <resource id="Magento_Backend::stores_settings">
                        <resource id="Magento_Backend::config">
                            <resource id="Mycompany_Mymodule::system_config" title="Token Integration" sortOrder="1000" /> <!-- This needs to be the same as the <resource> node in system.xml-->
                        </resource>
                    </resource>
                </resource>
            </resource>
        </resources>
    </acl>
</config>

config.xml You need to add a few nodes in your config.xml in order for Magento to automatically decrypt your value when read from the database.

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd">
    <default>
        <mycompany_mymodule>
            <configuration>
                <token backend_model="Magento\Config\Model\Config\Backend\Encrypted" />
            </configuration>
        </mycompany_mymodule>
    </default>
</config>

Now, you can use this in your code.

In your class constructor inject the config model

XML_PATH_TOKEN = 'mycompany_mymodule/configuration/token';

/**
 * Core store config
 *
 * @var \Magento\Framework\App\Config\ScopeConfigInterface
 */
protected $_scopeConfig;

/**
 * Initialize dependencies.
 *
 * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
 */
public function __construct(
    \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
) {
    $this->_scopeConfig = $scopeConfig;
}

/**
 * @return mixed
 */
public function getToken()
{
    return $this->_scopeConfig->getValue(
            self::XML_PATH_TOKEN,
            \Magento\Store\Model\ScopeInterface::SCOPE_STORE
        );
}

NOTES

  1. This example assumes that you are calling this code from the frontend of the site, and so does not explicitly provide the third parameter to he \Magento\Framework\App\Config\ScopeConfigInterface::getValue() method. If you wanted to make this more robust you would provide the scope ID as a method parameter to your method getToken and then pass it to getValue as the third parameter. Of course, you would need to know which scope ID to use. Typically this is done by pulling the website ID or store ID from some other object.
  2. Magento uses dependency injection to provide objects to other classes. Do not instantiate classes using the new keyword or the object manager.
Related Topic