Share changes with your team using Features and upgrade paths

Antonio De Marco
5 Jul 2010
4 Comments
Antonio De Marco
5 Jul 2010
4 Comments
Antonio De Marco, 5 Jul 2010 - 4 Comments

Share changes with your team using Features and upgrade paths

Meet hook_install() and hook_update_N()

The Features module is great for keeping configuration in code, with just a few clicks you can bundle your settings together and be sure that changes will be tracked and safely dumped to code at the next feature update.

However, often you will want to share other kinds of modifications with your team. Imagine you suddenly need to add a Taxonomy vocabulary or to enable a couple of modules your feature will be dependent on. Since your team's workflow is completely 100% database free, you cannot pass those changes by simply sharing your database. Still, you need to push those changes to other developers, who already enabled the feature you have been modifying in their development environment. How do you do that?

Make your workflow solid: meet hook_update_N()

Features are modules and modules can have their own upgrade path by implementing hook_update_N(). That's exactly what you need.

All the changes the Features module is not keeping track of must be stored in sequential implementation of the hook_update_N() to be sure that other developers will have them replicated in their database by simply visiting update.php.

Have a look at the following example: we want to add a free-tagging taxonomy vocabulary to our feature. First of all we need to enable the Taxonomy module add it as dependency in its .info file:

dependencies[] = "taxonomy"

Adding a dependency will not enable the new module on the other developers' local copy, since the feature has already been enabled and dependencies will not be rechecked. To be sure the module will be enabled we have to write an update function:

<?php
/**
* Enabling Taxonomy module.
*/
function feature_example_update_6001() {
 
$return = array();
 
drupal_install_modules(array('taxonomy'));
 
$return[] = array('success' => TRUE, 'query' => 'Enabling Taxonomy module.');
  return
$return;
}
?>

Now it's time to create our free-tagging vocabulary. Since taxonomies are still not supported by the Features module we have to find another way to put this change in code. One way to do it is to create the vocabulary in an update function:

<?php
/**
* Create "Tags" vocabulary.
*/
function feature_example_update_6002() {
 
$return = array();
 
$vocab = array(
   
'name' => 'Tags',
   
'multiple' => 0,
   
'required' => 0,
   
'hierarchy' => 0,
   
'relations' => 0,
   
'weight' => 0,
   
'nodes' => array('story' => 1),
   
'tags' => TRUE,
   
'help' => t('Enter tags related to your post.'),
  );
 
taxonomy_save_vocabulary($vocab); 
 
$return[] = array('success' => TRUE, 'query' => 'Create "Tags" vocabulary.');
  return
$return;
}
?>

We can now commit our changes. Once the other developers pull the latest version of our feature in their local environment all they need to do is to run a Drupal update by visiting update.php.

The comment on top of the update functions above plays an important role in the workflow since both Drush and update.php are actually able to display it when running drush updatedb or running update.php, giving valuable information to the other team members:

$ drush updatedb
The following updates are pending:

feature_example module              
6001 - Enabling Taxonomy module.
6002 - Create "Tags" vocabulary.

Do you wish to run all pending updates? (y/n):

Make your features database free: meet hook_install()

Storing your changes in hook_update_N() will allow your team to be always up-to-date with the latest development of your feature, making your development workflow solid and maintainable. But what happens if a new developer wants to hop in? Our rule of thumb is "No database sharing", hence the new developer needs to install our project from scratch and, still, we need to guarantee that he will get the complete latest status of the project. To do that we need to copy some of the configuration we have been placing in update functions to the hook_install():

<?php
/**
* Implementation of hook_install()
*/
function feature_example_install() {
 
$vocab = array(
   
'name' => 'Tags',
   
'multiple' => 0,
   
'required' => 0,
   
'hierarchy' => 0,
   
'relations' => 0,
   
'weight' => 0,
   
'nodes' => array('story' => 1),
   
'tags' => TRUE,
   
'help' => t('Enter tags related to your post.'),
  );
 
taxonomy_save_vocabulary($vocab); 
}
?>

Structural and development updates

As you might have noticed hook_install() copies code from the feature_example_update_6002() and not from feature_example_update_6001(). This is because the two updates have a different nature: 6002 is a structural update, meaning that it is something we must guarantee even if the feature will be installed from scratch; 6001 is a development update which only aims to upgrade an already working development copy.

When writing your upgrade paths, it's good practice to distinguish between two kinds of updates, and in the case of a structural update, make sure you copy changes to the hook_install() of your feature.

Real life examples

Upgrade and install hooks are really powerful, you can put virtually anything in there. Below we list some real life example of upgrade paths we use in our projects at Nuvole.

Add a menu and menu items

<?php
/**
* Implementation of hook_install()
*/
function master_site_install() {
 
 
// Create a custom menu called "Manage Content".
 
db_query("INSERT INTO {menu_custom} (menu_name, title, description)
            VALUES ('%s', '%s', '%s')"
,
           
'menu-content',
           
'Manage Content',
           
'Manage your site content.');

 
// Add "Home" menu item to Primary Links menu.
 
$item['link_title'] = t('Home');
 
$item['link_path'] = '<front>';
 
$item['menu_name'] = 'primary-links';
 
$item['weight'] = -10;
 
menu_link_save($item);
}
?>

Add OpenId to admin account

<?php
/**
* Add Nuvole OpenID to admin account.
*/
function nuvole_site_update_6004() {
 
$return = array();
 
// Delete any other association of the OpenId account to avoid conflicts.
 
$return[] = update_sql("DELETE FROM {authmap}
                          WHERE authname = 'http://nuvole.myopenid.com/'"
); 
 
// Bind OpenId account to admin user.
 
$return[] = update_sql("INSERT INTO {authmap} (uid, authname, module)
                          VALUES (1, 'http://nuvole.myopenid.com/', 'openid')"
); 
  return
$return;
}
?>

Upgrading data from OpenLayers 1.x to 2.x

<?php
/**
* Upgrade OpenLayers 1.x to 2.x: WKT data in "content_type_opera"
*/
function publicopera_site_update_6007() {

 
// OpenLayers 2.x stores WKT values as geometry collection.
  // Update data accordingly.
 
db_query("UPDATE {content_type_opera} SET field_opera_map_openlayers_wkt =
            CONCAT('GEOMETRYCOLLECTION(', field_opera_map_openlayers_wkt, ')')"
);
  return array(array(
'success' => TRUE, 'query' => 'All WKT content updated.'));
}
?>

Comments

Comments

Carlthuringer
6 Jul 2010

I've seen this workflow described several times by developers but as a novice dev implementing it in my own teams workflow is daunting because I don't know how to write the queries to implement the myriad of changes that I might make while developing a feature. Is there a methodfor watching the database that you would recommend that would allow me to make a set of changes and them dump the queries out for insertion into code? I'm afraid that trolling through html and mySQL to track everything down will cost me far more time than I would save with my small, 2-man team.

I've seen this workflow described several times by developers but as a novice dev implementing it in my own teams workflow is daunting because I don't know how to write the queries to implement the myriad of changes that I might make while developing a feature. Is there a methodfor watching the database that you would recommend that would allow me to make a set of changes and them dump the queries out for insertion into code? I'm afraid that trolling through html and mySQL to track everything down will cost me far more time than I would save with my small, 2-man team.

Carlthuringer, 6 Jul 2010

I've seen this workflow described several times by developers but as a novice dev implementing it in my own teams workflow is daunting because I don't know how to write the queries to implement the myriad of changes that I might make while developing a feature. Is there a methodfor watching the database that you would recommend that would allow me to make a set of changes and them dump the queries out for insertion into code? I'm afraid that trolling through html and mySQL to track everything down will cost me far more time than I would save with my small, 2-man team.

Carlthuringer, 6 Jul 2010
Antonio De Marco
6 Jul 2010

The Feature module indeed removes the necessity for you to work with SQL queries most of the times, since it monitors the DB changes and can automatically keep track of differences in code. For changes that are not tracked by Features you then need to use the method explained here.

However the SQL queries above were meant to explain how you can use upgrade path to manipulate your content, in case you need; in most cases you can just Drupal APIs as shown in feature_example_update_6001() and feature_example_update_6002().

The Feature module indeed removes the necessity for you to work with SQL queries most of the times, since it monitors the DB changes and can automatically keep track of differences in code. For changes that are not tracked by Features you then need to use the method explained here.

However the SQL queries above were meant to explain how you can use upgrade path to manipulate your content, in case you need; in most cases you can just Drupal APIs as shown in feature_example_update_6001() and feature_example_update_6002().

Antonio De Marco, 6 Jul 2010

The Feature module indeed removes the necessity for you to work with SQL queries most of the times, since it monitors the DB changes and can automatically keep track of differences in code. For changes that are not tracked by Features you then need to use the method explained here.

However the SQL queries above were meant to explain how you can use upgrade path to manipulate your content, in case you need; in most cases you can just Drupal APIs as shown in feature_example_update_6001() and feature_example_update_6002().

Antonio De Marco, 6 Jul 2010
Tanc
6 Jul 2010

Thanks for posting this, its very informative. I've recently implemented Features to push development updates to live using git with great success. So seeing this process for adding items that Features doesn't support through update hooks looks like it should fill most of the gaps quite nicely.

Thanks for posting this, its very informative. I've recently implemented Features to push development updates to live using git with great success. So seeing this process for adding items that Features doesn't support through update hooks looks like it should fill most of the gaps quite nicely.

Tanc, 6 Jul 2010

Thanks for posting this, its very informative. I've recently implemented Features to push development updates to live using git with great success. So seeing this process for adding items that Features doesn't support through update hooks looks like it should fill most of the gaps quite nicely.

Tanc, 6 Jul 2010
Adam Gerthel
3 Feb 2011

We're a small team of four, eager to move from developing on one shared server to local environments, but we're having problems figuring out exactly how to deal with this.

So far we want to use git + beanstalk to keep track of changes and use features + strongarm etc to "sync" our databases just like you do. We don't want to share database in case someone activates a module that the rest don't have in code, or if someone upload CCK files that the rest don't have in files.

But writing the feature updates like in your examples seems cumbersome, and especially difficult for us since we're at team of developers and themers. A designer working on tpl.php's, a couple of views and some css won't be happy having to write those kinds of queries all the time.

Is there an easy way to maintain this using for example ready made snippets? Or do you always write a new set of code for each update?

We're a small team of four, eager to move from developing on one shared server to local environments, but we're having problems figuring out exactly how to deal with this.

So far we want to use git + beanstalk to keep track of changes and use features + strongarm etc to "sync" our databases just like you do. We don't want to share database in case someone activates a module that the rest don't have in code, or if someone upload CCK files that the rest don't have in files.

But writing the feature updates like in your examples seems cumbersome, and especially difficult for us since we're at team of developers and themers. A designer working on tpl.php's, a couple of views and some css won't be happy having to write those kinds of queries all the time.

Is there an easy way to maintain this using for example ready made snippets? Or do you always write a new set of code for each update?

Adam Gerthel, 3 Feb 2011

We're a small team of four, eager to move from developing on one shared server to local environments, but we're having problems figuring out exactly how to deal with this.

So far we want to use git + beanstalk to keep track of changes and use features + strongarm etc to "sync" our databases just like you do. We don't want to share database in case someone activates a module that the rest don't have in code, or if someone upload CCK files that the rest don't have in files.

But writing the feature updates like in your examples seems cumbersome, and especially difficult for us since we're at team of developers and themers. A designer working on tpl.php's, a couple of views and some css won't be happy having to write those kinds of queries all the time.

Is there an easy way to maintain this using for example ready made snippets? Or do you always write a new set of code for each update?

Adam Gerthel, 3 Feb 2011

Comments

Add new comment

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.

Get your project started today!

Contact us