Drupal Painkillers: A-Z filter in Drpual 6 and Views

drupal_az_filter.pngToday I came across an interesting issue in Drupal and I wanted to share with you in case you'll ever have to deal with the same problem. I was going to create an index/glossary for some website using Views in Drupal 6. The index itself could be defined as follows:

  • It is a paged list of nodes of a given type (eg. index_item).
  • The list will be sorted alphabetically.
  • There will also be an exposed filter to allow us to filter the nodes by the first letter in their title (ie. a clickable alphabet from A to Z).

Creating a view

The first two features are very easy to implement, so I will describe them only briefly:

  1. Create a new Node view and call it eg. index.
  2. Add a new display (I chose page) and configure it (add a pager, URL path, menu entry and other details), add at least a Node title field.
  3. Set Sort criteria to sort by Node title ASC.
  4. Restrict the display to show Nodes of type index_item only - there is a Node type filter for that.

Your view is almost ready. To be able to filter the nodes by the first letter in their title, you still need to improve it a little by adding an argument:

  1. Add a Node title argument to the view display.
  2. Configure the argument in a following way:
    • Action to take if argument is not present: Display all values
    • Wildcard: all
    • Glossary mode: true
    • Glossary mode character limit: 1

Now if you save the view, you should be able to display the nodes according to the given argument. You can navigate to your view page and add eg. '/a' to the end of the page URL to show only nodes which have titles starting with the letter 'a'.

You can now create a menu for the whole alphabet to finish the job.

So what's the catch?

BUT what if some of your nodes are called like "1. Drupal article" - you would have to add another 10 items to the menu for each number from 0 to 9 - and that's certainly not what you want. It would be much better, if you could add just one 0-9 option (showing all nodes starting with a number).

drupal_az_filter_index

There is a neat sollution for that - create a Drupal module which would allow you to:

  1. Create a themeable A-Z filter (so you don't have to create a menu for that)
  2. Alter the view query (in order to support 0-9 option)

Creating a module

  1. Create a new module, call it eg. mymodule (I suppose you are familiar with that, see Module developer's guide otherwise).
  2. Add a hook_theme function to your module:
    /**
     * Implementation of hook_theme().
     */
    function mymodule_theme() {
        $items = array();
        $items['az_filter'] = array(
            'arguments' => array(),
            'function' => 'mymodule_az_filter',
        );
        return $items;
    }
  3. Create a theme function for the A-Z filter:
    function mymodule_az_filter() {
      $terms = array('0-9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'CH', 'I', 'J', 
                      'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 
                      'W', 'X', 'Y', 'Z', 'All');
     
      foreach($terms as $term) {
        $class = (arg(1) == strtolower($term) ? 'active' : '');
        $link = l(t($term), arg(0).'/'.strtolower($term));
        $rows[] = array('data' => $link);
      }
      return theme('item_list', $rows, NULL, 'ul', array('class' => 'az-filter'));
    }
  4. Add a hook_views_query_alter function that will to handle 0-9 option:
    /**
      * Implementation of hook_views_query_alter().
      */
    function mymodule_views_query_alter(&$view, &$query) {
        // Specific view only (change following 2 lines according to your needs)
        $view_name = 'index';
        $display_name = 'page_1';
        if ($view->name == $view_name && $view->current_display == $display_name) {
            // Obtain array indexes for both the clause and the argument
            $where_key = array_search("SUBSTR(node.title, 1, 1) = '%s'", 
                                      $query->where[0]['clauses']);
            $arg_key = array_search("0-9", $query->where[0]['args']);
            if ($where_key !== FALSE && $arg_key !== FALSE) {
                // Use REGEXP to is_numeric test
                $query->where[0]['clauses'][$where_key] = 
                    "SUBSTR(node.title, 1, 1) REGEXP ('[0-9]')";
                // unset the argument since we are not using it
                unset($query->where[0]['args'][$arg_key]);
                // reindex the arguments array
                $query->where[0]['args'] = 
                    array_values($query->where[0]['args']);
            }
        }
    }
  5. After you've activated your new module, you must add a following PHP line to the template with the view or put it in a PHP input block:
    print theme('az_filter');

This will render the A-Z filter itself. Now you have a working A-Z filter with 0-9 option support. Note that I also added an 'All' option to the filter, which would make the view display all the nodes regardless of the first letter in their title.

Comments

hey, thanks for sharing this. I want to create only alphabetical filter for view of users. will surely try this.