Drupal Views Tutorials - Exporting Views to Code

Today we will look at creating a module to allow you to export your views to code. While this concept has been explored on other blogs previously, several important end steps that I find particularly useful have been left out of those discussions. I would like to remedy that here, detailing an entire process for setting up the module, displaying use scenarios, and also showing the changes that this module will make to your views administration process.

The first thing to discuss is why you would even want to export your views to code. There are a few good reasons to do this, but the most obvious one (and the one I personally find the most important) is "protection." Protection of your views and your site.

Standard views don't work until after they're saved on the site. However, since there is no such thing currently as "View Revisions," if the view ends up not functioning (or worse yet, breaks the entire site!) after saving the view, you cannot just go back to a previous version. You typically have to scrap the entire view and start from scratch. If you're just trying to make a list of content, that's not a huge deal, but if you have extremely complicated views with contextual filters, relationships, and accessing multiple parts of your site, this can at best be a headache, and at worse cost you many hours of work.

Storing your views in code allows you to have a safe place to keep a working "backup" of the view. Obviously you can just export your view and save it in a notepad file if you choose, but the method we're going to use not only backs it up, but it uses the backup as a module to display the view.

The Drupal 7 and Drupal 6 methods for this are both extremely similar. I will note the one difference below, but to be clear, this blog is speaking as though you are using Drupal 7 and Views 3.

This blog assumes you know how to create a basic custom module to use as a foundation. If you do not know how to create a basic custom module, check out our quick tutorial on the subject.

1. Hooking into views

Hooks in Drupal are just ways of modifying the website page's results.
Let's create one in our new module.

There are two hooks we will be using in this exercise: hook_views_api and hook_views_default_views. These links will take you to the drupal api pages that describe each hook.

Now let's edit the custom_example.module, and make its contents the following: <?php /* * Implementation of hook_views_api() */ function custom_example_views_api() { return array('api' => 3.0); }

This is all you need to do inside the module file. Note the api of "3.0". Here is where you will make the one change I mentioned above if you are using Drupal 6. Since Views 3.0 was never backported to Drupal 6, you will instead name "2.0" as the api. Here is the code: <?php /* * Implementation of hook_views_api() */ function custom_example_views_api() { return array('api' => 2.0); }

That's it. Now comes the fun part.

2. Providing our views

Now we will hook into 'hook_views_default_views' - basically, the purposes of this hook is to allow a module to provide views that can be read and understood by drupal. This is the main part of the blog that diverges from most other blogs on this subject. Typically, descriptions of how to export views to code end with this section, and the view is input into the hook function. However, that leaves this function very large (with a lot of view information in the function), and also makes it much more difficult to have multiple views. So the steps I will take here will be for the purpose of leaving the function clean, and providing us with a method for having as many views as you want.

Create a new file titled 'custom_example.views_default.inc' and place it in the same folder as your module. Then post the following into the file and look over the comments: <?php /** * Implements hook_views_default_views(). **/ function custom_module_views_default_views() { //Finds all files that match a given mask in a given directory //In our case, looks for any files named *.view in the /views directory $files = file_scan_directory(drupal_get_path('module', 'custom_module'). '/views', '/.view/'); foreach ($files as $filepath => $file) { require $filepath; if (isset($view)) { $views[$view->name] = $view; } } //Check that there are views in the directory //This keeps the site from throwing errors if there are no views to return if ($views) { return $views; } }

What this new hook does is it tells drupal to look into this directory that we've named (/views) and see if there are any files inside the directory using the format *.view. If there are, it takes the name of that view and sets it as the default version of that view. This will make more sense once we get a little further along, but just note that basically, this hook is telling drupal to look for your views in code.

3. Create the /views directory and start a template

Now you need to create the /views directory that is shown in the hook above. Inside your module directory, create a new folder called "views." Now create a new file and place in that directory named "view_template.view." This is an optional step, but one I find useful. Inside the template, simply paste the following: <?php //Paste Exported views Code here //Name file according to the "views_name" in exported view //Example: this_is_my_view_name.view

I use this template in a very simple way - anytime I export a new views code, I copy the exported information and paste it over my comments in the template file. Then I save-as the file with the appropriate view name.

4. An example and the "Revert" option

Let's make a quick example. You just need a list of all content on the site (note, your site already does this, but this is an example that you could literally copy from here and paste into your code and it will work, because I am using no custom cck fields, arguments, roles, etc). Open your template file created above (view_template.view), and copy the following code into the file (you can copy over the commented instructions if you like), and save the file as "all_content.view" (note that the file name is identical to value in the key-value pair on the 2nd line - $view-name = 'all_content'): $view = new view(); $view->name = 'all_content'; $view->description = ''; $view->tag = 'default'; $view->base_table = 'node'; $view->human_name = 'all_content'; $view->core = 7; $view->api_version = '3.0'; $view->disabled = FALSE; /* Edit this to true to make a default view disabled initially */ /* Display: Master */ $handler = $view->new_display('default', 'Master', 'default'); $handler->display->display_options['use_more_always'] = FALSE; $handler->display->display_options['access']['type'] = 'perm'; $handler->display->display_options['cache']['type'] = 'none'; $handler->display->display_options['query']['type'] = 'views_query'; $handler->display->display_options['exposed_form']['type'] = 'basic'; $handler->display->display_options['pager']['type'] = 'full'; $handler->display->display_options['style_plugin'] = 'default'; $handler->display->display_options['row_plugin'] = 'fields'; /* Field: Content: Title */ $handler->display->display_options['fields']['title']['id'] = 'title'; $handler->display->display_options['fields']['title']['table'] = 'node'; $handler->display->display_options['fields']['title']['field'] = 'title'; $handler->display->display_options['fields']['title']['label'] = ''; $handler->display->display_options['fields']['title']['alter']['word_boundary'] = FALSE; $handler->display->display_options['fields']['title']['alter']['ellipsis'] = FALSE; /* Sort criterion: Content: Post date */ $handler->display->display_options['sorts']['created']['id'] = 'created'; $handler->display->display_options['sorts']['created']['table'] = 'node'; $handler->display->display_options['sorts']['created']['field'] = 'created'; $handler->display->display_options['sorts']['created']['order'] = 'DESC'; /* Filter criterion: Content: Published */ $handler->display->display_options['filters']['status']['id'] = 'status'; $handler->display->display_options['filters']['status']['table'] = 'node'; $handler->display->display_options['filters']['status']['field'] = 'status'; $handler->display->display_options['filters']['status']['value'] = 1; $handler->display->display_options['filters']['status']['group'] = 1; $handler->display->display_options['filters']['status']['expose']['operator'] = FALSE;

Now here is an important step. Your new view will not show up until the views cache on your site has been cleared. In Drupal 7, go to '/admin/structure/views/settings/advanced' and click the "Clear views' cache" button. In Drupal 6, go to '/admin/build/views/tools' and click the "Clear views' cache" button. Now, when you go back to your list of views, you will see your new view. You should notice one new change as well. If you hover over the options for what you can do with that view, you will see a new option, called "Revert." It will have taken the place of the "Delete" option on any views you had the option for deleting. By clicking this button, it will delete any changes that have been made to the view on the site, and will revert the view back to its default position, which will be the view as it is in code (such is the power of the hook_views_default_view hook). This means, once you have a view that is working, you can save it to code, flush your views' cache, and then edit that view to your heart's content, knowing that if you ever mess anything up too terribly, you can always "Revert" the view back to its working glory. Or for that matter, on a production site, you can allow your clients with limited knowledge of views to attempt to create and edit their own, knowing that if something is messed up, you can easily have it fixed.

I have included two images so you can see what the view would have looked like on the views list page before flushing caches, and what it looks like afterward. Note the difference between "Delete" and "Revert":

Websmiths.co Views Tutorial - Delete Option Before Flushing Cache

Websmiths.co Views Tutorial - Delete Option After Flushing Cache

(Note: When you click "Revert," on the confirmation page that follows, it will still say "Are you sure you want to delete this view?" - this is okay, it is just the wording that was used. If on the views list page, it said "Revert" instead of "Delete," you are safe to revert.)

And that's all there is to do.