Keeping views under control

It's no secret that version control on Drupal is hard. Specifically, the problem of iteratively building a site on a staging server, then moving it to a running production system has been the source of countless wasted hours, lost data, pulled hair, blog posts, and modules promising a solution. On Twitter, some would say #drupal #migration #fail.

This post is not the answer to this problem. I just want to show actual code that helps with a small part of it, namely keeping views under version control. The approach I show here is also used by the Drupal.org customizations module, so you're in good hands :-)

Among its countless innovations, Views introduced the concept of exportable configuration, which allows a view to be exported to code, instead of living in the database. Just click the [export] link on a view and you're shown a piece of code that you can copy/paste to an import screen on another Drupal installation, or that you can place in a mymodule.views_default.inc file which Views will pick up next time you clear your cache. This last feature lets you keep your views in a revision control system, which:

  • frees you from having to recreate it or import it manually
  • reduces the chance of version conflict between your dev and live sites
  • tracks the changes that occurred to the view

To take advantage of this feature to manage 10s of views on my day job site, I've come up with a little mechanism that makes it easy to individually modify and update views. Here's what I do:

  • Maintain a site-specific module (let's call it "mysite.module"). That's where all the business-specific code lives.
  • Build and test the view on the dev site (let's call it "myview").
  • Once a view is tested, export it to a file called mysite/views/myview.view.inc, as such:
<?php
// @file myview.view.inc

$view = new view;
$view->name = 'myview';
...
?>
  • Use the following piece of code to automatically pass all such views to the Views module:
<?php
// @file mysite.views_default.inc

/**
* Implementation of hook_views_default_views().
*/
function mysite_views_default_views() {
 
$views = array();
 
$files = file_scan_directory(drupal_get_path('module', 'mysite') . '/views', '(.*).view.inc$');
  if (
$files) foreach ($files as $absolute => $file) {
    require(
$absolute);
   
$views[$view->name] = $view;
  }
  return
$views;
}
?>

That's it! Using this approach, you should be able to completely get rid of the Views UI module on your live site, ensuring that your version control process is rigorous (for Views at least).

P.S. This approach can readily be generalized for CCK, Panels, Rules, and other modules that use the same configuration export approach. For others, the CTools Exportables module can be used to add the export mechanism. Thanks merlinofchaos, for the millionth time!!

Comments

This works nicely in a number of modules.

I would like to add this as an option in Views so that all you need to do is declare a default views directory and have this happen.

Also, if you use hook_views_default_views_alter(&$views) instead of hook_views_default_views() you can override in-code views from other modules. Use that only for site specific modules but for that use it is fabulous.

I was thinking about that automatic export mechanism as well. Another item for the endless TODO list?

Thanks for the info about hook_views_default_views_alter, that's a valuable tip that I will try very soon.

I've been meaning to do the whole views-in-code thing for a while and your post made it super simple. Got thrown off for a minute by // @file mysite.default_views.inc. Should it be // @file mysite.views_default.inc?

Glad to be of use :-) Thanks for notifying me about the error, I fixed that.

Features is probably the right way to go.

Caveat: this approach will NOT work if you're overriding an existing code-based view. Specifically, you will not be able to declare your overridden view with the same name. My solution was to simply rename the view, so my advice is to never override a default view, but rather clone it from the beginning.

For a better solution, check out merlinofchaos' comment above. Basically, use hook_views_default_views_alter(&$views) for views that were originally code-based before you overrode them.

Using hook_views_default_views_alter(&$views) to manage import/export of overridden views is a cool idea. Since this procedure operates on the views array by reference the implementation is clearly different from hook_views_default_views(). Can someone point me to some example code?

Although I haven't used this technique myself, googling "hook_views_default_views_alter" returned a few examples of this hook in use, although none overwriting the whole view.