Hard and Soft configuration in Drupal Distributions
Features, taxonomy and other beasts
An extract from the new teaching materials we are preparing for our DrupalCon Denver 2012 pre-conference training, Code-Driven Development: Use Features Effectively.
By now the advantages of a clean Code-Driven Development workflow are clear to the majority of Drupal developers; things like separation between configuration and content, packaging and deployment of new site functionalities or sharing changes in a distributed team don't look that scary anymore.
Still not everything in Drupal can be clearly classified into configuration or content, such as Taxonomy terms. Taxonomy terms are mostly used to categorize our content so, naturally, they are often used in site configuration: a context that reacts to a given taxonomy term is a fairly common scenario. Taxonomy terms can be generated by users (think about free-tagging), which makes them fall into the "Content realm". Drupal, in fact, considers them as such, assigning to each term a unique numeric identifier, and that's the problem.
Each component has its own name
One of the turning points in making configuration exportable in code was the introduction of a unique string identifier for each component: this way it's easy to share configuration and to avoid conflicts. So what happens with taxonomy terms? If they appear in our context conditions then we'd better bundle them together: no matter where we are going to deploy our feature, they will always have to have the same values. That's simply not possible: they have numeric IDs, forget about easy life here.
Various attempts have been made to export numerically identified components, such as UUID Features Integration or the handy Default Content module, but they only solve part of the problem: we want other components to know the unique name of our terms.
Hard and Soft configuration
At Nuvole we adopted the following terminology to classify the two kind of settings that we need to deal with everyday:
- Hard configuration includes the settings under the distribution developer's control (e.g., Views or Contexts); it has a machine name, it is easy to export, it gives no headache. We store it in Features.
- Soft configuration includes the settings that are meant to be overridden by the site administrator (e.g., the default theme, or the initial terms of a vocabulary); it often gets a unique numeric ID from Drupal, it is impossible to export safely, it is painful to handle. We store it in the Installation profile.
This distinction becomes fundamental when the configuration is altered or (if you are dealing with a distribution) when the underlying distribution is upgraded. In the case of Hard configuration, altering it results in an overridden feature, which is upgrade-unsafe. In the case of Soft configuration, altering it does not change the Features state, since the corresponding settings are stored in the Installation profile, and changes are upgrade-safe.
Not only taxonomy terms
The distinction between Hard and Soft configuration goes beyond how to conveniently export taxonomy terms: it is more a design decision, especially important when dealing with distributions. We consider everything that the site builder might be entitled to change as Soft configuration. The place where to usually store Soft configuration is your hook_install()
; this guarantees a safe upgrade path to the next version of the distribution. An example could be the default theme: you may ship your distribution with a specific theme but the site owner might want to change it and subsequent updates shouldn't alter it.
Here is how our profile hook_install()
might look like in a Drupal 7 distribution:
<span style="color: #000000"><span style="color: #0000BB"><?php<br><br></span><span style="color: #FF8000">/**
<br> * Implements hook_install()<br> */<br></span><span style="color: #007700">function </span><span style="color: #0000BB">example_install</span><span style="color: #007700">() {</span><span style="color: #0000BB">
 <br> $terms </span><span style="color: #007700">= array(); </span><span style="color: #0000BB">
 <br> $vocabulary </span><span style="color: #007700">= </span><span style="color: #0000BB">taxonomy_vocabulary_machine_name_load</span><span style="color: #007700">(</span><span style="color: #DD0000">'category'</span><span style="color: #007700">);</span><span style="color: #0000BB">

 <br> $terms</span><span style="color: #007700">[] = </span><span style="color: #DD0000">'Solution'</span><span style="color: #007700">;</span><span style="color: #0000BB">
 <br> $terms</span><span style="color: #007700">[] = </span><span style="color: #DD0000">'Client'</span><span style="color: #007700">;</span><span style="color: #0000BB">
 <br> $terms</span><span style="color: #007700">[] = </span><span style="color: #DD0000">'Use case'</span><span style="color: #007700">;</span><span style="color: #0000BB">

 <br> </span><span style="color: #007700">foreach (</span><span style="color: #0000BB">$terms </span><span style="color: #007700">as </span><span style="color: #0000BB">$name</span><span style="color: #007700">) {<br> </span><span style="color: #0000BB">$term </span><span style="color: #007700">= new </span><span style="color: #0000BB">stdClass</span><span style="color: #007700">();<br> </span><span style="color: #0000BB">$term</span><span style="color: #007700">-></span><span style="color: #0000BB">vid </span><span style="color: #007700">= </span><span style="color: #0000BB">$vocabulary</span><span style="color: #007700">-></span><span style="color: #0000BB">vid</span><span style="color: #007700">;<br> </span><span style="color: #0000BB">$term</span><span style="color: #007700">-></span><span style="color: #0000BB">name </span><span style="color: #007700">= </span><span style="color: #0000BB">$name</span><span style="color: #007700">;<br> </span><span style="color: #0000BB">taxonomy_term_save</span><span style="color: #007700">(</span><span style="color: #0000BB">$term</span><span style="color: #007700">);<br> }<br> <br> </span><span style="color: #FF8000">// Enable custom theme
 <br> </span><span style="color: #0000BB">theme_enable</span><span style="color: #007700">(array(</span><span style="color: #DD0000">'example_theme'</span><span style="color: #007700">));<br> </span><span style="color: #0000BB">variable_set</span><span style="color: #007700">(</span><span style="color: #DD0000">'theme_default'</span><span style="color: #007700">, </span><span style="color: #DD0000">'example_theme'</span><span style="color: #007700">); <br>}</span><span style="color: #0000BB">
<br>?></span></span>
Reference for site builders
If you are customizing a distribution and you need to override some of its default Hard configuration you might want to have a look at the Features Override module and at these two related articles from Phase2 Technology:
Tags: Code Driven DevelopmentDrupal PlanetDrupalConTraining