Cms: Allow plugin configs to override settings more automagically

Created on 31 Jan 2017  路  15Comments  路  Source: craftcms/cms

Created by: Timothy Kelty ([email protected]) on 2015/04/02 12:33:05 +0000
Votes at time of UserVoice import: 3


It seems a common pattern to define all plugin configuration as "settings" (in the db, configurable though cp), but have the all be overridable via a plugin config file.

It's easy enough to do, but I think it would be nice if this was either: the default (my preference), or just easier to flip on.

Currently I do something like:

public function getSettings()
{
  $settings = parent::getSettings();
  foreach ($settings as $name => $value) {
      $configValue = craft()->config->get($name, 'pluginHandle');
      $settings->$name = is_null($configValue) ? $value : $configValue;
  }

  return $settings;
}

But it would be great if I could just set:

    public function allowSettingsConfigOverrides()
    {
        return true;
    }

Or instead of true you could even set an array of keys to allow overriding for.

Most helpful comment

This would be really nice, so that we could override anything on a per-environment basis. If this was baked into the Craft 3 craft\base\Plugin->getSettings() then all plugins could be overridden by default.

All 15 comments

I'd argue that this should be the default behavior with plugin settings.

It would also be nice if there were a way to defineSettings directly in your config.php, as boilerplate code like this gets tedious:

``` protected function defineSettings()
{
return [
'disqusShortname' => [AttributeType::String, 'label' => 'Disqus Site Short Name', 'default' => craft()->config->get('disqusShortname', 'disqus')],
'useSSO' => [AttributeType::Bool, 'label' => 'Use Single Sign On', 'default' => craft()->config->get('useSSO', 'disqus')],
'disqusPublicKey' => [AttributeType::String, 'label' => 'Disqus Public Key', 'default' => craft()->config->get('disqusPublicKey', 'disqus')],
'disqusSecretKey' => [AttributeType::String, 'label' => 'Disqus Secret Key', 'default' => craft()->config->get('disqusSecretKey', 'disqus')],
'customLogin' => [AttributeType::Bool, 'label' => 'Use Custom Login/Logout URLs', 'default' => craft()->config->get('customLogin', 'disqus')],
'loginName' => [AttributeType::String, 'label' => 'name', 'default' => craft()->config->get('loginName', 'disqus')],
'loginButton' => [AttributeType::String, 'label' => 'button', 'default' => craft()->config->get('loginButton', 'disqus')],
'loginIcon' => [AttributeType::String, 'label' => 'icon', 'default' => craft()->config->get('loginIcon', 'disqus')],
'loginUrl' => [AttributeType::String, 'label' => 'url', 'default' => craft()->config->get('loginUrl', 'disqus')],
'loginLogoutUrl' => [AttributeType::String, 'label' => 'logout', 'default' => craft()->config->get('loginLogoutUrl', 'disqus')],
'loginWidth' => [AttributeType::String, 'label' => 'width', 'default' => craft()->config->get('loginWidth', 'disqus')],
'loginHeight' => [AttributeType::String, 'label' => 'height', 'default' => craft()->config->get('loginHeight', 'disqus')],
];
}

You can clean this up a bit, but it's still a lot of boilerplate that would be a nice default: 

protected function defineSettings()
{
    $settings = [
        'disqusShortname' => [AttributeType::String, 'label' => 'Disqus Site Short Name'],
        'useSSO' => [AttributeType::Bool, 'label' => 'Use Single Sign On'],
        'disqusPublicKey' => [AttributeType::String, 'label' => 'Disqus Public Key'],
        'disqusSecretKey' => [AttributeType::String, 'label' => 'Disqus Secret Key'],
        'customLogin' => [AttributeType::Bool, 'label' => 'Use Custom Login/Logout URLs'],
        'loginName' => [AttributeType::String, 'label' => 'name'],
        'loginButton' => [AttributeType::String, 'label' => 'button'],
        'loginIcon' => [AttributeType::String, 'label' => 'icon'],
        'loginUrl' => [AttributeType::String, 'label' => 'url'],
        'loginLogoutUrl' => [AttributeType::String, 'label' => 'logout'],
        'loginWidth' => [AttributeType::String, 'label' => 'width'],
        'loginHeight' => [AttributeType::String, 'label' => 'height'],
    ];

    $settings = array_map(function($key, $value) {
        $value['default'] = craft()->config->get($key, 'disqus');

        return $value;
    }, array_keys($settings), $settings);

    return $settings
}

```

This would be really nice, so that we could override anything on a per-environment basis. If this was baked into the Craft 3 craft\base\Plugin->getSettings() then all plugins could be overridden by default.

I'm a big fan of this.

I think the biggest win here is predictability for plugin users - knowing they can always set any setting via config, regardless of how the plugin is authored.

Even more useful would be if it could tell you that the setting was being overridden by the config file, the way Craft does This would probably need to be on the Settings model however

Some kind of Craft provided a way to bottleneck and standardize all of this would be really helpful.

Plugin developers wouldn't have to reinvent the wheel, and all plug-ins would operate in an understood and predictable manner

Even more useful would be if it could tell you that the setting was being overridden by the config file, the way Craft does This would probably need to be on the Settings model however

Agreed, this lets plugin authors display that settings are being overridden when displaying the CP field. Currently authors seem to implement this themselves for every plugin.

+1!

Slipped this in over the weekend, along with lots of other changes to the Config service. Going forward, all plugins that want to have settings will need to create a settings model and instantiate it with a protected createSettingsModel() method, and that model will be populated with whatever is in the DB (if the plugin has CP settings), merged with whatever is in config/pluginhandle.php (if it exists).

This is a breaking change for plugins that were previously providing default values in config.php and calling the Config service to get their PHP file-based settings. That config.php file can now be deleted (move the default values into the settings model), and calls to Craft::$app->config->get('settingName', 'pluginhandle') should be replaced with MyPlugin::getInstance()->settings->settingName.

Here鈥檚 an example of how our Element API plugin was affected by the changes, if that helps: https://github.com/craftcms/element-api/commit/dd65ee480c8a69fb474f577c76deeb00d82b4ac8

I assume that a pluginhandle.php file in the craft/config folder is still how to allow people to override things in a multi-environment friendly manner?

@khalwat, yeah there's just no need to set your defaults in the pluginhandle/config.php any longer.

Geez, @brandonkelly said it right there in his original comment, too. I don't know how I missed that.

@timkelty I'll probably still include the config.php file for people to rename & move into craft/config, so they know what settings they can change.

The one thing this doesn't solve is a plugin being able to report when a setting is overriding something in the db via a pluginhandle.php file.

@khalwat Craft doesn鈥檛 define the HTML for the CP plugin settings; plugins do. So it would be up to the plugin to add those warnings if they wanted.

Plugins can find which keys the config is defining like so:

$overrideKeys = array_keys(Craft::$app->config->getConfigFromFile('pluginhandle'));

or it might make more sense to get it directly from your settings template:

{% set overrideKeys = craft.app.config.getConfigFromFile('pluginhandle')|keys %}

Yeah the macro I've been using in the past is:

{% macro configWarning(setting) -%}
    {% if (craft.config.get(setting, 'disqus')) |length %}
        {{ "This is being overridden by the {setting} config setting in your disqus.php config file." |t({
            setting: setting
        }) |raw }}
    {% endif %}
{%- endmacro %}
{% from _self import configWarning %}

I'll figure out how to adapt it once it rolls out in beta 8

One caveat to be aware of though 鈥撀爄f a setting is defined in the config file, the file鈥檚 value is going to override the DB-stored value, so your plugin (including its settings page) will never know what the actual DB-stored value is.. This is also the case when editing volume settings in the CP, if you override them in config/volumes.php. The CP settings will show the PHP file鈥檚 values, and if you save them, the PHP file鈥檚 values will end up in the DB as well.

Was this page helpful?
0 / 5 - 0 ratings