Configuration and Settings
Overview
Config is stored in yml files so it can be checked into git. It is loaded into the config table of the database for performance. Use drush config-import
(or drush cim
) for this purpose. Config includes database table definitions, views definitions and lots more. You can even use config to store a little setting indicating your site is in a test
mode which can trigger displaying some useful information that only you can see.
Config files should be stored in a non-web accessible directory and specified in settings.php
or settings.local.php
like:
$settings['config_sync_directory'] = '../config/sync';
You can override config items in a settings.php
or settings.local.php
using the $config
global variable.
More on configuration Management on Drupal.org.
Reading config values in code
This example shows how to load a rest endpoint from config. This is very similar to Drupal 7 variable_get()
.
Use the Configuration API main entry point \Drupal::config()
to load the config item and then use get()
to retrieve the value you want. Config can have multiple values in a single yml file.
$pizzaEndpoint = \Drupal::config('pizza_academy_core.pbx.rest.endpoint');
$pizza_service_url = $pizzaEndpoint->get('pizza_rest_endpoint').$reg_id;
When you export the config, this information is stored in a file called pizza_academy_core.pbx.rest.endpoint.yml
with a key pizza_rest_endpoint
.
The contents of the file are simply:
pizza_rest_endpoint: 'https://pbx.pizza.com/pbx-profile-service/'
You'll find this in a file in the config sync directory specified in settings.php
e.g.
config/sync/pizza_academy_core.pbx.rest.endpoint.yml
The config sync directory location is specified in settings.php
or settings.local.php
like this
$settings['config_sync_directory'] = '../config/sync';
More on creating custom modules: Using your own configuration
For testing, you can override config items in a settings.php
or settings.local.php
using the $config
global variable.
Writing config values in code
Here are some examples of writing config values in code. This is similar to Drupal 7 variable_set()
.
$config = \Drupal::configFactory()->getEditable('pizza_academy_core.pbx.rest.endpoint');
$config->set('pizza_rest_endpoint', $pizza_service_url);
$config->save();
// Disable auto refresh if it is enabled.
$auto_refresh_enable = \Drupal::config('tea_teks_voting.settings')
->get('conditional_refresh_refresh_enable');
if ($auto_refresh_enable) {
\Drupal::configFactory()->getEditable('tea_teks_voting.settings')
->set('conditional_refresh_refresh_enable', 0)
->save();
}
Add config to an existing module
You simply create a yml file in the module's /config/install
directory.
The config file should start with the module name then a period and the thing you want to store the config about. So modulename.something.yml
e.g.dir_salesforce.cron.yml
for cron information, dir.funnelback.yml
for funnelback information or tea_teks_spr.testing.yml
for testing information.
If the module name is pizza_academy_core and the thing you want to store config about is the pbxpath, first create a file called: pizza_academy_core.pbxpath.yml
.
The yml filename is passed as a parameter into calls like \Drupal::config('...')
without the .yml
extension. e.g. if the filename is danamod.header_footer_settings.yml
then use:
$config = \Drupal::config('danamod.header_footer_settings');
Here we add some configuration to a module called pizza_academy_core:
In docroot/modules/custom/pizza_academy_core/config/install/pizza_academy_core.pbxpath.yml
We have a file with the contents:
url: 'https://pbx.pizza.com/'
langcode: 'en'
To deploy your config, you can:
- Copy it to the
config/sync
directory - Paste the contents into the config Drupal u/i or
- Import it into the db with drush.
The drush way is the easiest in my opinion.
drush config-import --source=modules/custom/pizza_academy_core/config/install/ --partial -y
Then you can access it from a controller at docroot/modules/custom/pizza_academy_core/src/Controller/VerifyCertificationPage.php
using the following code:
$pbx_path_config = \Drupal::config('pizza_academy_core.pbxpath');
$pbx_path = $pbx_path_config->get('url');
$pbx_achievements_url = $pbx_path . "achievements?regid=".$reg_id;
Once you grab the url, you can use it later in your code.
Add a config form to a custom module
To create a config form, see the config forms in the forms chapter.
Import config changes in your module via drush
During module development, you might find you want to add some configuration. This is very useful as part of that workflow as you can repeat it as you continue making changes.
drush @dev2 config-import --source=modules/migrate/test1/config/install/ --partial -y
Note. the @dev2 is a site alias. See Drush alias docs for more info. These are sooo useful.
Config Read Only
Many sites can benefit from the use of the Config Read Only module. This module allows you to set some config items to be read only. This is useful for things like the site name, email address, etc. which should not be changed by the user. It is also useful for things like the site uuid which should not be changed on a production site.
To set a site in read-only mode, add the following to your settings.php
or settings.local.php
file:
$settings['config_readonly'] = TRUE;
When you try to change a config item, you will see a message indicating that: This form will not be saved because the configuration active store is read-only.
You can permit changes to specific forms (and their config) by adding something like the following to your settings.php
or settings.local.php
file (more below):
$settings['config_readonly_whitelist_patterns'] = [
'system.menu.main*',
'system.menu.utility*',
'system.menu.footer*',
'system.menu.learn-more*',
'system.menu.for-publishers*',
'system.file',
'webform.webform.*',
...
];
Once you clear caches and have configured the whitelist, you will no longer see the message and you can change the config items you specified above.
To lock production and not other environments, your code in settings.php might be a conditional on an environment variable like (for Acquia hosted sites):
if (isset($_ENV['AH_SITE_ENVIRONMENT']) && $_ENV['AH_SITE_ENVIRONMENT'] === 'prod') {
$settings['config_readonly'] = TRUE;
}```
The following approaches are somewhat discouraged since they may allow anyone with Drush or shell access to bypass or disable the protection and change configuration in production.
To allow all changes via the command line and enable readonly mode for the UI only:
```php
if (PHP_SAPI !== 'cli') {
$settings['config_readonly'] = TRUE;
}
You could similarly toggle read-only mode based on the presence or absence of a file on the webserver (e.g. in a location outside the docroot).
if (!file_exists('/home/myuser/disable-readonly.txt')) {
$settings['config_readonly'] = TRUE;
}
Whitelist settings for custom modules
In the following example, we use this code to load config for our custom module:
$refresh_enabled = \Drupal::config('tea_teks_voting.settings')->get('conditional_refresh_refresh_enable');
This means the settings.php needs to have the following to allow site admins to change the value of conditional_refresh_refresh_enable
in the UI:
$settings['config_readonly_whitelist_patterns'] = [
'tea_teks_voting.*',
];
See the following documentation for more information on whitelisting.
Config Storage in the database
Config is also kept in the config table of the database.
The name field stores the config id e.g. views.view.infofeeds (the definition of a view called infofeeds)
The data field stores the stuff in the config serialized into a blob
Add some config to site config form
Here we modify the site config form and add a phone number. This code is in a .module
file.
use Drupal\Core\Form\FormStateInterface;
/**
* Implements hook_form_FORM_ID_alter().
*/
function mymodule_form_system_site_information_settings_alter(&$form, FormStateInterface $form_state) {
$form['site_phone'] = [
'#type' => 'tel',
'#title' => t('Site phone'),
'#default_value' =>
Drupal::config('system.site')->get('phone'),
];
$form['#submit'][] = 'mymodule_system_site_information_phone_submit';
}
The $form['#submit']
modification adds our callback to the form's submit handlers. This allows our module to interact with the form once it has been submitted. The mymodule_system_site_information_phone_submit
callback is passed the form array and form state. We load the current configuration factory to receive the configuration that can be edited. We then load system.site
and save phone based on the value from the form state.
function mymodule_system_site_information_phone_submit(array &$form, FormStateInterface $form_state) {
$config = Drupal::configFactory()->getEditable('system.site');
$config->set('phone', $form_state->getValue('site_phone'))
->save();
}
Don't forget there is a module called config pages which might save you some coding if you need to add some config to a site.
Override config in settings.php
This can be useful for local development environment (where you might put these changes into settings.local.php) or on each one of your servers where you might need some configuration to be slightly different. e.g. dev/test/prod.
Drupal allows global $config
overrides (similar to drupal 7) The configuration system integrates these override values via the Drupal\Core\Config\ConfigFactory::get()
implementation. When you retrieve a value from configuration, the global $config variable gets a chance to change the returned value:
// Get system site maintenance message text. This value may be overriden by
// default from global $config (as well as translations).
$message = \Drupal::config('system.maintenance')->get('message');
To override configuration values in global $config
in settings.php, use a line like this (which references the configuration keys:
$config['system.maintenance']['message'] = 'Sorry, our site is down now.';
For nested values, use nested array keys
$config['system.performance']['css']['preprocess'] = 0;
If you have a configuration change, for example, you have enabled google tag manager. When you export the config drush cex -y
and git diff
to see what changed in config, you'll see (in the last 2 lines) that status is changed from true to false.
$ git diff
diff --git a/config/sync/google_tag.container.default.yml b/config/sync/google_tag.container.default.yml
index 39e498c99..375bfb8af 100644
--- a/config/sync/google_tag.container.default.yml
+++ b/config/sync/google_tag.container.default.yml
@@ -1,6 +1,6 @@
uuid: 5919bbb9-95e3-4d8b-88c8-030e6a58ec6c
langcode: en
-status: false
+status: true
To put this in settings.php
or settings.local.php
, add a line and set the value to true or false:
$config['google_tag.container.default']['status'] = false;
Using a testing variable in config for a project
First create the yml file in your module/config/install
e.g. tea_teks_srp.testing.yml
with this as the contents:
test_mode: FALSE
This will be the default state of the app
In the Drupal U/I under config, devel, configuration synchronization, import, single item i.e. at /admin/config/development/configuration/single/import
select simple configuration
. In the configuration name field, put tea_teks_srp.testing
.
Paste in the text of the file
test_mode: FALSE
and import. This will load the new value into the database.
Then in your docroot/sites/default/settings.local.php
(to enable testing features) add
$config['tea_teks_srp.testing']['test_mode'] = TRUE;
This will override your config you added above so test_mode is true.
Then to use the test_mode, you can load it into a controller class (or form class) from the config (and the value in the settings.local.php
will override the default) with the following:
$test_mode = \Drupal::config('tea_teks_srp.testing')->get('test_mode');
And then just use the $test_mode
variable as needed e.g.
if ($this->test_mode) {
$value = $this->t("Reject Citation $citation_nid");
}
Getting and setting configuration with drush
Here we are fiddling with the shield module settings
In config, synchronize, we see an item: shield.settings
So we can load it with drush:
$ drush cget shield.settings
credential_provider: shield
credentials:
shield:
user: nistor
pass: blahblah
print: 'Please provide credentials for access.'
allow_cli: true
_core:
default_config_hash: c1dcnGFTXFeMq2-Z8e7H6Qxp6TTJe-ZhSA126E3bQJ4
Drilling down deeper, let's say we want to view the credentials section. Notice that drush requires a space instead of a colon:
$ drush cget shield.settings credentials
'shield.settings:credentials':
shield:
user: nistor
pass: blahblah
Now to get down to the user name and password. And we are adding period back in. Huh?
$ drush cget shield.settings credentials.shield
'shield.settings:credentials.shield':
user: nistor
pass: blahblah
and finally:
$ drush cget shield.settings credentials.shield.pass
'shield.settings:credentials.shield.pass': blahblah
So if you want to set these:
drush cset shield.settings credentials.shield.pass yomama
Do you want to update credentials.shield.pass key in shield.settings
config? (y/n): y
And
drush cset shield.settings credentials.shield.user fred
Do you want to update credentials.shield.pass key in shield.settings
config? (y/n): y
And there is that message
drush cget shield.settings print
'shield.settings:print': 'Please provide credentials for access.'
And so
drush cset -y shield.settings print "Credentials or I won't let you in"
and while we're here, we could always put these into the $config
object via settings.php
(or settings.local.php
:
$config['shield.settings']['credentials']['shield']['user'] = "nistor";
$config['shield.settings']['credentials']['shield']['pass'] = "blahblah";
Similarly, for setting stage_file_proxy origin:
drush config-set stage_file_proxy.settings origin https://www.mudslinger.com
Config Pages module
Config Pages is a really useful module which allows you to quickly create some config
along with forms to control them.
Here is a screenshot of 4 bool
fields defined in the Config Pages user interface.
Here is the data entry screen:
Here is the code to load the values from the config.
use Drupal\config_pages\Entity\ConfigPages;
/**
* Load custom settings from the ConfigPages custom_settings entity.
*/
protected function loadCustomSettings(): void {
$custom_settings_entity = ConfigPages::config('custom_settings');
$this->logBatchVoteTime = $custom_settings_entity->get('field_log_batch_vote_time')->value;
$this->logSingleVoteTime = $custom_settings_entity->get('field_log_single_vote_time')->value;
$this->displaySingleVoteTime = $custom_settings_entity->get('field_display_single_vote_time')->value;
$this->displayBatchVoteTime = $custom_settings_entity->get('field_display_batch_vote_time')->value;
if ($this->logBatchVoteTime ||
$this->logSingleVoteTime ||
$this->displaySingleVoteTime ||
$this->displayBatchVoteTime) {
$this->shouldComputeElapsedTime = TRUE;
}
}
{:.note } From their docs page There are other three ways to load the configuration values:
// This example uses side-loading for simplicity.
$config_pages = \Drupal::service('config_pages.loader');
$entity = $config_pages->load($config_page_machine_name, $optional_context);
// This example uses call to a static method load:
$entity = ConfigPages::config($config_page_machine_name);
// This example uses storage manager to get a config page via storage manager:
$storage = \Drupal::entityTypeManager()->getStorage('config_pages');
$entity = $storage->load($config_page_machine_name);
Each of these methods, will return a loaded entity with a given active context.
Drush config commands
Drush will provide you with all the tools you need to fiddle with config from the command line. Check out the drush docs
Viewing config
Note
If you override config values in your settings.php, when you view them with drush cget, drush will ignore values overidden in settings.php. This can be confusing. More below.
drush config:get system.site
- displays the system.site config.drush config:get system.site page.front
- displays what Drupal is using for the front page of the site: e.g.
$ drush config:get system.site page.front
'system.site:page.front': /node
Note
cget
is short for config:get
.
Viewing overridden config values
When you view the value in config, drush confusingly
will ignore values overidden in settings.php.
drush cget narcs_infoconnect.imagepath basepath
This displays the basepath that is in the Drupal database. If you override the basepath in settings.php, you have to use the special flag --include-overridden
to see the overridden value.
drush cget narcs_infoconnect.imagepath basepath --include-overridden
Also drush can execute php for a little more fun approach:
drush ev "var_dump(\Drupal::configFactory()->getEditable('system.site')->get('name'))"
or
drush ev "print \Drupal::config('narcs_inferconnect.imagepath')->get('basepath');"
Delete from config
cdel is short for config:delete.
drush @dev2 cdel migrate_plus.migration.test1
drush @dev2 cdel migrate_plus.migration_group.default
Check what has changed with config:status
cst
is short for config:status
drush cst
--------------------------------------------- ------------
Name State
--------------------------------------------- ------------
admin_toolbar.settings Only in DB
admin_toolbar_tools.settings Only in DB
automated_cron.settings Only in DB
block.block.bartik_account_menu Only in DB
block.block.bartik_branding Only in DB
block.block.bartik_breadcrumbs Only in DB
block.block.bartik_content Only in DB
...
Only in DB
means the config has not yet been exported. Best practice is to check the config info git for loading onto the production site. Usually you would use drush cex
at this point to export the config and add it to git.
After exporting drush will report that everything has been exported and that there are no differences between the database and the sync folder.
drush cst
[notice] No differences between DB and sync directory.
Export entire config
cex
is short for config:export
This will dump the entire config, a bunch of yml files into the config sync folder which is specified in settings.php
(or settings.local.php
) as
$settings['config_sync_directory'] = '../config/sync';
drush cex -y
- export entire config.
$ drush cex -y
[success] Configuration successfully exported to ../config/sync.
../config/sync
Import config changes
If you change the site name (for example) by mistake and want to restore it , you can re-import the values from the last export.
First check what changed with drush cst
then use drush cim -y
to restore the config to it's previous glory. cim
is short for config:import
.
Drupal cleverly notices which config items have changed and loads only those changes into the database.
$ drush cst
------------- -----------
Name State
------------- -----------
system.site Different
and
$ drush cim -y
+------------+-------------+-----------+
| Collection | Config | Operation |
+------------+-------------+-----------+
| | system.site | Update |
+------------+-------------+-----------+
// Import the listed configuration changes?: yes.
[notice] Synchronized configuration: update system.site.
[notice] Finalizing configuration synchronization.
[success] The configuration was imported successfully.
Views config.yml files
For views, the config filenames are be in the form views.view.infofeeds
for a view called infofeeds
.
Troubleshooting
Config export
Sometimes when you try to export config, it seems to randomly decide to delete a lot of config items although drush config cst
shows just a few items have changed. In this circumstance, drush config cim -y
will try to import those few items.
I've found that the problem is related to this setting in the settings.local.php
(or settings.php
)
$settings['config_exclude_modules'] = ['devel', 'stage_file_proxy', 'masquerade'];
For some reason, an edge condition is reached which confuses the configuration engine in Drupal. Commenting out the above line resolves the issue.
I hope this one saves you countless hours of frustration. I know it has caused me plenty of frustration!