Providing Default Values for Views’ Contextual Filters

When I need to filter a view based on some elaborate condition, I often find myself using Drupal Views' contextual filters. Normally this kind of filter is supposed to pull the filter value from the URL, but it also lets you select other methods of finding a value if it’s not in the URL. For instance, if the filter is for the author, you can configure the view to use the current user’s ID. Even though this is very flexible, you may occasionally need filtering that cannot be set up with the available options. To overcome the limitations, you can add your own filtering logic by developing a Views plugin for this purpose.

This post will discuss how you can write a Views plugin that uses custom logic to feed a default value to a contextual filter. In a hypothetical example, we will build a view that lists articles that are categorized in the logged-in user’s favorite category.

To get started, we will need a taxonomy vocabulary for categories, and term reference fields in both the article node type and the user entity that reference that vocabulary. Let’s call the field in the user entity field_user_favorite_category, and the field in the node type field_category. So we want to filter for nodes whose value in the field_category field matches that of the current user’s field_user_favorite_category field.

Since we are writing a Views plugin, we’ll need to implement hook_views_api() to let Views know that it needs to look for plugins in our module.

/**
 * Implements hook_views_api().
 */
function mymodule_views_api() {
  return array("api" => "3.0");
}

The next step is to declare our new plugin to Views. This can be done in hook_views_plugins(). The implementation needs to live in mymodule.views.inc, and looks as follows:

/**
 * Implements hook_views_plugins().
 */
function mymodule_views_plugins() {

  $plugins = array(
    'argument default' => array(
      'mymodule_favorite_category' => array(
        'title' => t('User\'s favorite category'),
        'handler' => 'views_plugin_argument_mymodule_users_favorite_category',
      ),
    ),
  );

  return $plugins;
}

The keys in the returned array are plugin types, and their values are arrays of plugin definitions. The hook above declares that we have a plugin of type “argument default”, which is the type that can supply default values to contextual filters, and the plugin’s name is mymodule_favorite_category. The "title" value will appear in the contextual filter configuration, and the "handler" value specifies the name of the PHP class to use for this plugin.

The plugin implementation will live in a separate file named after its class. To follow Views’ conventions, this file will be views_plugin_argument_mymodule_users_favorite_category.inc. It needs to be declared in the module’s .info file in order for it to be loaded as follows.

name = "Mymodule"
description = "Mymodule's description"
core = 7.x
files[] = views_plugin_argument_mymodule_users_favorite_category.inc

Finally, on to the implementation of the plugin. The plugin class will extend Views’ class for the same purpose, views_plugin_argument_default, so that it has all the functionality that Views expects of it. We will only need to override one method, get_argument(), which actually determines and returns the default value.

/**
 * Default argument plugin to provide the user's favorite
 * category as default.
 */
class views_plugin_argument_mymodule_users_favorite_category extends views_plugin_argument_default {

  function get_argument() {
    global $user;

    // Load the full user object to get access to the fields.
    $account = user_load($user->uid);

    $favorite_values = field_get_items('user', $account, 'field_user_favorite_category');
    if ($favorite_values) {
      // Field is single value; get the only item from the array.
      return $favorite_values[0]['tid'];
    }
  }

}

To make use of the new plugin, add a contextual filter for field_category in your view.

In the contextual filter configuration, select the "Provide default value" option. After doing so, you should see the new “User’s favorite category” option in the “Type” dropdown list. When this option is selected, the view should only show nodes that have the same taxonomy term as the one selected in the current user’s field_user_favorite_category field.