How to use the Drupal 8 configuration system

Error message

  • Deprecated function: implode(): Passing glue string after array is deprecated. Swap the parameters in drupal_get_feeds() (line 394 of /usr/www/users/gdd/heyrocker/includes/common.inc).
  • Deprecated function: The each() function is deprecated. This message will be suppressed on further calls in menu_set_active_trail() (line 2405 of /usr/www/users/gdd/heyrocker/includes/menu.inc).

NOTE

This article is now obsolete and very much out of date. For current status and information on the configuration API for Drupal 8, please visit the official handbook pages.


Having just committed our shiny new configuration system to core, there is one question on everyone's mind - how do I use it? What do I need to do to convert my modules? How do I configure all the things? Well you're in luck because I am here to tell you all this information! If I've done this right, then by the time you're done reading this, you should be ready to convert your modules to the new system (and maybe some core modules as well, huh?)

If you are at DrupalCon, you can also come to the code sprint room (room 601) every day at 1:00 for a personal version of this session presented by me! I would also be remiss if I didn't mention that we will be running a code sprint Friday all day! The perfect opportunity to come and convert some modules!

Overall Architecture

The configuration system employs a three-level architecture:

Level 1: Signed file storage

At the lowest level, all configuration data will be stored on-disk using signed XML files in a randomly named config directory.

Individual configuration files (prefix.example.xml) will look like the following:

<?xml version="1.0"?>
<config>
  <some_string>Woo kittens!</some_string>
  <some_int>42</some_int>
  <some_bool>1</some_bool>
</config>

How these files are organized and named is still under discussion (see http://drupal.org/node/1479492 for conversation on this topic), however it is likely that the structure will vary quite a bit depending on use cases. Core will do files like system.site_information.xml, and something like Flag which other modules hook into to provide "discoverable" stuff might do both a module-specific configuration file module.flag.xml for "global" module settings, as well as provide flag.$flagname.xml files.

One advantage of storing configuration this way rather than the current system is that we can limit the data we load on each request; in the current system we have to load the entire contents of the variable table into memory on each page request.

Level 2: Active Store

This layer moves the configuration data from the file system to something that can be read and accessed much more readily. For the vast majority of Drupal sites this will be database storage, but for high-performance sites could be something like MongoDB or Redis. This is called the “active store.”

In the default configuration, this will push data into a central table called "config":

CREATE TABLE config (
   name varchar(255) NOT NULL DEFAULT '' COMMENT 'The identifier for the  configuration entry, such as module.example (the name of the file,  minus .xml).',
  data longtext NOT NULL COMMENT 'The raw XML data for this configuration entry.',
  PRIMARY KEY (name),
);

While at first glance the structure looks like the "variables" table in Drupal 7 and below, the fundamental difference is this table stores configuration objects (say, every site information setting: name, mission etc) while the variable stored single values (like the site name). Also, as said above, we are not loading the whole table in memory. Additionally, this provides a separation from the files which is necessary for reload operations and also allows sites to be protected from improperly built configuration files, which can cause site errors.

In regular site operation, all configuration data gets read out of the active store. The data here gets updated on two conditions

  • UI changes (automatic): When the save button is clicked on an admin page, data gets written to the active store.
  • Code changes (manual): When migrating configuration from dev to prod, for example, the underlying files will have changed, but the data in the database table will not. Data will continue to be read from the active store until site admins reload the configuration on disk into the active store via an administrative interface and/or a drush command. (Note: this functionality is still being implemented, follow this issue to keep up on the progress: http://drupal.org/node/1447686)

Level 3: Configuration API

At this level are the actual API functions that module developers will interact with in order to manipulate configuration values; essentially, a replacement for variable_get()/variable_set().

// Load a set of configuration out of the active store.
// 'prefix.name' refers to the filename of the .xml file, without the extension.
$config = config('prefix.name');
// Access a single value out of the store.
echo $config->get('my.value');
// Change a value and save it back to both the active store and the filesystem.
$config->set('my.value','my new value');
$config->save();

I'm a module developer. How do these proposed changes affect me?

Declaring your own configuration options

In the past, configuration was declared in a mish-mash of ways. system_settings_form() would automagically save all form elements as variables in the variable table. Modules like Flag and Views would employ a hook_X_default() type of pattern, and so on. Under the new system, declaring your own "variable" style configuration options happens in xml files shipped with your module. These files will live in a ‘config’ directory that lives in your module directory. You declare the defaults for these variables in just this one place, as opposed to every time you try and retrieve them.

An example file might look like:

image.styles.thumbnail.xml:

<?xml version="1.0"?>
<config>
  <name>thumbnail</name>
  <effects>
    <image_scale>
      <data>
        <width>100</width>
        <height>100</height>
        <upscale>1</upscale>
      </data>
      <weight>0</weight>
    </image_scale>
  </effects>
</config>

During module installation, the configuration file will be copied to a user's config directory, and from then on the site-specific settings will be stored there. Similarly, when a module is disabled, these config settings would be removed from the live directory. Defining default views, flags, etc. will be done much the same, ultimately gettinf stored in the active store and .xml files.

variable_set()/variable_get() are going away

In Drupal 7 and below, all variables are global, so accessing and saving them is done this way:

// Load the site name out of configuration.
$site_name = variable_get('site_name', 'Drupal');
// Change the site name to something else.
variable_set('site_name', 'This is the dev site.');

In Drupal 8, configuration will only be lazy-loaded when needed. The above code would therefore change as follows:

// Load the site name out of configuration.
$config = config('core.site_information');
$site_name = $config->get('site_name');
// Change the site name to something else.
$config->set('site_name', 'My Awesome Site');
$config->save();

For "discoverable" chunks like entities or views, you can load all "sub-configuration" files (e.g. list of all views, list of all node types) with the following call (exact API call TBD): config_get_names_with_prefix('entity.node.');. This will return something like array('entity.node.page', 'entity.node.article');. So to retrieve all settings for the page node type run config('entity.node.page').

But my configuration data is way more complex than that!

Because we are using XML as a storage format, configuration objects can be arbitrarily complex. However we can not currently support storing objects in the XML, only arrays.

Migrating configuration from dev to prod

The overall workflow here would be as follows:

  1. On your development server, perform whatever configuration changes are needed through the UI. Create Views, check checkboxes, etc. These changes will get written to *both* the database table *and* the file system so the two are in sync (it will also re-generate the digital signatures of the underlying files).
  2. When finished with your edits, review the changes in the config directory with $vcs diff (or your tool of choice) to confirm they look as expected.
  3. If everything looks good, move the changed files to your production server in the usual way (SFTP, $vcs add/commit/push, $vcs update/pull). Nothing will immediately change, as your site will still be reading from the active store.
  4. Finally, go to admin/configuration/system/config or run drush config-update (note: final location/command likely different; making these up right now). This will outline the files that have changed. If the changes seem good, go ahead and confirm to overwrite the content of the active store with the on-disk configuration. The admin tool will offer to regenerate the file signatures if necessary.

An example

I am the maintainer of the Super Kitten module. This module allows me to create a customized kitten as my site's logo. You can set its color, its fur length, and what its name is. Right now this module exists for Drupal 7 and I want to update it for Drupal 8! Here is what would need to be done. NOTE: This assumes you are a Drupal developer and have done some module coding. I don't go into a lot of the nitty grtitty of module dev or explaining hooks or anything here.

Step 1: Create default config

Right now I store three variables, which are saved via a config screen using system_settings_form(). I need an xml file representing this data. So first I will create a directory in my module's root called 'config'. In there I will create an XML file which I will call super_kitten.settings.xml. My use case is pretty simple so I give it the following format and with the following default values:

<?xml version="1.0"?>
<config>
  <kitten_color>orange</kitten_color>
  <kitten_fur_length>long</kitten_fur_length>
  <kitten_name>Rodney</some_sound>
</config>

This part is the same for all config files:

<?xml version="1.0"?>
<config>
...
</config>

Everything in between <config> amd </config> is up to you!

OK that was easy, now what?

Step 2: Convert your config form

Unfortuantely, the configuration system does not currently support using system_settings_form() (issue at http://drupal.org/node/1324618). So you will have to write your own submit function to handle saving your settings. Right now I have super_kitten_settings_form*() and I need to add super_kitten_form_submit(). Next, I need to remove my call to system_settings_form() and add my own submit button. OK that is pretty simple. Now you need to write your submit function. This will involve three steps

  1. Create a config object with your data
  2. Set your new values
  3. Save your new data

So lets look at what this function might look like.

// Create a config object, initialized with our data. We pass in our
// filename, without the file extension.
$config = config('super_kitten.settings');

// Now set the data for each of our settings
$config->set('kitten_color', $form_state['values']['kitten_color']);
$config->set('kitten_fur_length', $form_state['values']['kitten_fur_length']);
$config->set('kitten_name', $form_state['values']['kitten_name']);

// And save
$config->save();

That is pretty much the complete workflow for interacting with config data. Note that you do actually have to explicitly save. If you don't then nothing will happen.

Step 3: Set form so that it properly sets default values

Another thing we need to do is setup our form so that when it is viewed, it properly displays the current data we have saved. In previous versions of Drupal, we would use variable_get() for this, like so:

$form['kitten_color'] = array(
  '#type' => 'textfield',
  '#title' => t('Kitten color'),
  '#default_value' => variable_get('kitten_color', 'orange'),
);

With the config system, we will do something like this:

$config = new config('super_kitten.settings');
$form['kitten_color'] = array(
  '#type' => 'textfield',
  '#title' => t('Kitten color'),
  '#default_value' => $config->get('kitten_color'),
);

So once again, we create a config object to work with, and then in #default_value we get our data out of the config object. One thing to note is that you can no longer set a default value in get(). All config objects must be pre-populated with default data that can be retrieved when the data is first accessed.

So while the syntax changes a bit, in general, saving and retrieving data is very similar to the way that variable_get() and variable_set() worked.

Step 4: Convert any other code accessing these variables

This is really just an extension of step 3. Anyplace you had

variable_get('kitten_name')

becomes

$config = config('super_kitten.settings');
$config->get('kitten_name');

Step 5: Write an upgrade path

Given your newfound knowledge, this should mostly be pretty simple, with the exception that variable_get() and variable_set() will be gone, so you will have to get that data out of the database by hand. Your update hook will look like this:

$config = config('super_kitten.settings');

$kitten_name = db_query('SELECT value FROM {variable} WHERE name = 'kitten_name')->fetchField();
$config->set('kitten_name', $kitten_name);
db_delete('variable')->condition('name', 'kitten_name')->execute();

// Repeat for each variable

$config->save();

And that's it! Your module has now been converted! And guess what else? You now know much of what you need to know to convert core systems to the new configuration management system! But how would you ever do that? It's simple! Go to this URL:

http://drupal.org/project/issues/search/drupal?issue_tags=Config%20novice

That is the list of issues that is currently labeled 'Config novice' aka the things that are pretty simple implementations of core subsystems. This is a easy way to further configuration management for core, and the best part is you just learned everything you need to know! Awesome isn't it? See you in the queue!

AttachmentSize
Image icon config_api.png48.83 KB
I wrote two chapters of this book - Drupal 7 Module Development and I co-wrote it with Matt Butcher, Larry Garfield, Matt Farina, Ken Rickard, and John Wilkins. Go buy a copy!
I am the owner of the configuration management initiative for Drupal 8. You can follow this work at the dashboard on groups.drupal.org.

I used to work at NodeOne in Stockholm, Sweden. NodeOne is the largest pure Drupal consultancy in Europe. They have built websites for clients like IKEA, SFBio, and Möbler. If you need some work done get in touch!