November 2008

Speed Up and Version Your Views

Since getting started with Drupal over two years ago, the sites I’ve built with it have naturally gotten bigger and bigger in scope. As your sites get bigger and bigger, you always look for ways to keep your site running as smoothly as possible, and this usually ends up meaning getting rid of queries wherever you can.

One feature of Views which is often used by module developers is the ability of a module to expose a set of default views. The calendar module, for example, provides a default calendar view in both its Drupal 5 and 6 versions. This is an obvious asset for developers of contributed modules: if your module interfaces with Views, it makes sense to provide a default view that users can modify.

Implementation and Potential Benefits

The hook used to do this is hook_views_default_views(). Since most modern sites are run off of a number of Views, you can also realize several benefits by building your sites with a custom module that implements hook_views_default_views():

  1. It improves performance. Views implemented via hook_views_default_views() do not require a database query to instantiate. You will realize an even greater performance gain if you also use an opcode caching system such as APC or XCache.
  2. Providing views in code will allow you to override the default view to make changes, and you can then choose to keep the changes in the database, update the module file to reflect the changes, or revert back to the version in your module file.
  3. Because the view (and theoretically, changes to that view) are stored in files, you can put them in version control and see how the views used in your site change over time and revert to an earlier version without having to go to a database backup.

Views2 / Drupal 6 Implementation

Here’s how to do it in Views2 on Drupal 6. When implementing a site, we at Tree House usually end up with a utility module that will do some of the things that will need to do theming or development tweaks, like the odd hook_form_alter(), or provide a Views handler or two. I’ll be calling this module treehouse_utils. There’s two steps to providing a default view in Drupal 6 and Views2: you need to tell Views2 that you implement the Views2 API, and then you have to create a file that contains the default view(s).

So first, in treehouse_utils.module, it’s time to tells Views2 that we’re implementing Views features with hook_views_api(). The implementation of the hook looks like this in our module:

function treehouse_utils_views_api() {
  return array(
    'api' => 2,
    'path' => drupal_get_path('module', 'treehouse_utils'),
  );
}

This module just tells Views2 that we’re implementing version 2 of the Views API and that it should look for Views-related files in the directory of the treehouse_utils module. (Some other modules will make a separate directory called includes where its Views-related files will live.)

Putting the Views in Code

The other task that we need to do is to implement hook_views_default_views() and actually tell Drupal about our Views. It is possible to do this in the main treehouse_utils.module file, but the trend in Drupal 6 is towards using more files so that Drupal has less code to load and parse overall on each page request. You can implement hook_views_default_views() (as well as a smattering of other Views-related hooks) in a MODULENAME.views.inc file. In the case of hook_views_default_views(), you can implement that in its own file, MODULENAME.views_default.inc.

So here’s what treehouse_utils.views_default.inc will look like:

<?php

function treehouse_utils_views_default_views() {
  $views = array();

  // Start copy and paste of Export tab output.

  // End copy and paste of Export tab output.

  // Add view to list of views to provide.
  $views[$view->name] = $view;

  // ...Repeat all of the above for each view the module should provide.

  // At the end, return array of default views.
  return $views;
}

Implementations of hook_views_default_views() are expected to return an array, so for each view that you would like to have provided by a module, go to the Views2 administrative interface, click Export, and copy and paste the PHP code between the two comments, and also be sure to include the $views[$view->name] = $view; line, where the newly-defined view object gets included in the array that will be returned.

Finally, when your module is enabled (or when you next visit the Views2 administration page or otherwise clear Views2’s caches,) you’ll see the following:

Notice that because the view which I put into the module is also in the database, Views tells us that the view is overriden - a default is now provided in PHP code, but our version in the database still supercedes it. If you were to click Revert, the database entry for the view would be deleted, and Drupal would load it from your MODULENAME.views_default.inc file.

Continuing this Workflow

At this point, you can check your module into version control and have a record of your view at this point in time. Later, you could make some more changes in the Views2 UI, hit Export again, and paste the newer version into the file, check that into version control and continue.

Views2 is amazing, and these techniques let you squeeze a little more performance and a lot more possibilities for revision control while building out your sites.

Also, be sure to vote for Thomas’s DrupalCon session if you’d like to look at concrete techniques of all types to scale your Drupal sites!

Comments

If you enable the views_export module, you get the bulk export tool. This makes it easy to just pick a whole bunch of views and export them all at once for inclusion into this file.

These are great tips.

If you store a lot of views in the same module and you rely on the bulk export tool, you'll find that versioning your views is difficult because "overriden" and "normal" views (those which are stored in the database) are put in front of the other "default" views for your module. This means that your views are not versioned in any useful way, because the new versions of your views are always on top of the exported block of code.

I submitted a patch to Views 2 that you might want to try that kinda solves this problem. If you don't change the names of your views, you can always rely on an alphabetical sort before exporting the views to a module.

http://drupal.org/node/325607

I think also that all drupal views are cached in the database, so there's probably not a performance gain. This was the case in Views 1 for sure, and Views 2 is still caching all views in its cache table, not just "normal" stored views.

That's true AFAIK, but for each view you use on a page, you still have to get it from the cache, which is a DB query. Using the file method, you have one file read from the system, and that's it.

I think that's right? :)

Thanks for the great tips. Just what I needed at this moment... great timing.

Regards,
Mark

The 'path' key you're returning in treehouse_utils_views_api() is not necessary. It's the default :)

Other than that super minor detail, nice writeup! :)

Thanks, Wim - that's a good point.

(Since this was a quick-and-dirty example, I didn't change from that default, but a lot of modules do add an 'includes' directory onto that path, so I thought I'd put it in.)

Thanks for this. I always wanted to know how to create views programatically and versioning them definitely makes sense.

Glad you enjoyed this, Chris. This technique would have been a great help in some of my earlier D6 / Views2 work.

I really became acquainted with it while working on the SonyBMG Multisite project, so I must give Earl Miles the credit for turning me on to this. He thinks this is "the way to deploy views," and I agree.

I have tried to follow your example but when I add my module the view does not appear in my list of views.

The contents of myview.module are

<?php

function myviews_api()
return array('api' => 2 );

The contents of myviews.views_default.inc are

<?php

function myviews_views_default_views()

views = array();
........

views[view->name] = view;
return views;

where ...... contains the exported view that I previously created.

I can't figure out why this doesn't work. What am I missing ?

Thanks

Jack,

You're very close here - the problem with with the .module file.

The views API hook is called hook_views_api(), so your function should be named function myviews_views_api().

Give that a shot and let me know if it works.

Thanks for this article, it is just what I was looking for. I think I'll be deploying all my views this way in the future, since then they are portable and not stuck in the DB where they are hard to back up. My question is how can you provide a default theme for the views as part of the module? Is this possible? Some of these complex views are nothing without their theme code to go with them, and I'd like to be able to port that between sites.

I'm really very useful to follow a long-time see this as a blog here.Thank you for your valuable information oyun...

Post new comment

The content of this field is kept private and will not be shown publicly.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <hr> <blockquote> <h3> <h4> <h5> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Lines and paragraphs break automatically.
  • You can enable syntax highlighting of source code with the following tags: <pre>, <c>, <cpp>, <drupal5>, <drupal6>, <java>, <javascript>, <php>, <python>, <ruby>. The supported tag styles are: <foo>, [foo].

More information about formatting options

By submitting this form, you accept the Mollom privacy policy.
Stay Informed

Sign up now for the Treehouse Newsletter.