Drupal 7 Tutorial: Creating Custom Filters in Views

I love views. It makes my job easier, so I can focus on the more complex things, rather then having to hand-write queries, create forms for filtering data and it saves time debugging my typos. Every so often you will get a request that can't be accomplished out of the box.

If you are trying to customize the options in an exposed form, you may still need to do hook_form_alter(). Or, if you are trying to filter or sort your view by a field that doesn't appear in views, this short tutorial will show you how.

Say your module has a table that has a relationship to files, although it could be to nodes, users, taxonomy, etc. All your module needs to do is define two little functions hook_views_api() and hook_views_data(). The documentation there is pretty good, but lets take a look at a simple real-life example.

<?php 
/**
 * Implements hook_views_api()
 */
function my_video_views_api() {
  return array(
    'api' => 3,
  );
}
 
/**
 * Implements hook_views_data()
 * Exposes our playcount table to views
 */
function my_video_views_data() {
  // Basic table information.
  $data['my_video_playcount'] = array(
    'table' => array(
      'group' => t('File statistics'),
      'join' => array(
        'file_managed' => array(
          'left_field' => 'fid',
          'field' => 'file_fid',
        ),
      ),
    )
  );
 
  // Our fields
  $data['my_video_playcount']['playcount'] = array(
    'title' => t('Playcount'),
    'help' => t('The total number of times the video has been started.'),
  );
 
  // Adds our field in the "Fields" section of Views
  $data['my_video_playcount']['playcount']['field'] = array(
    'handler' => 'views_handler_field_numeric',
    'click sortable' => TRUE,
  );
 
  // Adds our field in the "Filters" section of Views
  $data['my_video_playcount']['playcount']['filter'] = array(
    'handler' => 'views_handler_filter_numeric',
  );
 
  // Adds our field in the "Sort" section of Views
  $data['my_video_playcount']['playcount']['sort'] = array(
    'handler' => 'views_handler_sort',
  );
 
  return $data;
}

I've tried to make the code comments self-explanatory, but let's break it down a bit. The hook_views_data() function expects an array. The first dimension is keyed by your table name.

// Basic table information.
  $data['my_video_playcount'] = array(
    'table' => array(
      'group' => t('File statistics'),
      'join' => array(
        'file_managed' => array(
          'left_field' => 'fid',
          'field' => 'file_fid',
        ),
      ),
    )
  );

This first section defines the 'group' the relationship will be displayed in, you could use an existing group, like 'Content' or define your own. The 'join' defines the relationship between your table and the primary table of the view, in our case, the file_managed table. The 'left_field' will be the column in the file_managed table, and the 'field' is the column in my_video_playcount table.

 // Our fields
  $data['my_video_playcount']['playcount'] = array(
    'title' => t('Playcount'),
    'help' => t('The total number of times the video has been started.'),
  );

Next you define the fields in your table, in our case we only have one field 'playcount'. The 'title' and 'help' are displayed in the Views admin pages when you add or edit your filters. You can then define how each field may be used in views.

// Adds our field in the "Fields" section of Views
  $data['my_video_playcount']['playcount']['field'] = array(
    'handler' => 'views_handler_field_numeric',
    'click sortable' => TRUE,
  );

Defining 'field' will make it appear in the "Fields" section, you will need to set the 'handler', which can be the name of any subclass of views_handler_field. You can set 'click sortable' to TRUE to make it sortable in the Table display.

 // Adds our field in the "Filters" section of Views
  $data['my_video_playcount']['playcount']['filter'] = array(
    'handler' => 'views_handler_filter_numeric',
  );

Similarly you can add 'filter' and 'sort' support for your field by defining the 'handler' that the field should use. Views provides some built in handlers, that should meet almost every use-case, but if not, all the handlers are extendable, so you can quickly write your own if necessary. I haven't needed to write my own handler, and couldn't think of one for the purpose of this blog post, but I'm sure there are. If you have one, I'd love to hear about it.

Thanks Jonathan for sharing this great tutorial. I have a few questions about views and I am wondering if there are answers to them.

1) I found out that Views Search does not provide exact match on the substring level, i.e. if you set the Search filter to 'Contains" and search for 'communication', it will only get rows with communication only, but no such strings for example:
Communications committee, Communication council, etc.

2) Also, if you set it "Contains any word' and you search for 'can', you will get results as such: cannes, cannibal, canopy, etc

3) If you expose filters, you get all criteria out there, but one of them is 'Is Noy Empty' or so, and if a user selects this, it will get all data stored in your database. This way, any one can scrape your data. How to customize the exposed filters and remove some of them if you wish?

What is the best settings for a real advanced search on Drupal website using views?

One last thing: is there a way to use highlight with searched terms? i.e. if you search for 'medical', it will display highlighted in yellow for example in the Search results. Any ideas?

Thanks,
Mohamed

1) I think you mean that if your filter is set to "Contains" and you search for "communication" you get results that contain "communications committee", "communications council", etc. If however you search for "communications committee" you only get results for "communications committee". This is because contains basically does a query "WHERE field LIKE '%{$input}%'" (NOTE: This is an example, and not safe for real use, input needs to be sanitized first, see the Drupal 7 database API).

2) The "Contains any Word" option does the same thing as #1, except it splits up the words you enter, so "communications committee" will become something like "WHERE (field LIKE '%communications%' OR field LIKE '%committee%')". If your word is "can" it will not only match "can", "canopy", etc, but also "toucan" and "buccaneer"

3) Sounds like you are exposing the operator, and my first thought is that you'll need to use hook_form_alter() as described in this post. That post is for Drupal 6, but the same concepts should apply.

4) The Drupal search module does this by default, although I think it does full-word matches only, so "communication" will not match "communications". I haven't played with this, but the Drupal search_excerpt() function looks like a good place to start to see how search module does it.

I badly need a custom filter for views which would test a taxonomy term against the number of direct children. I know PHP but I'm perplexed as per the handlers to be used. Any chance you could elaborate more on this, please?

Hi,
The query I have might sound lame, but its just for knowledge. Can we customize the exposed filters in views. I have 2 filters and I want to place them in different columns of table. Is it possible?

Thanks in advance,
Swathi

I posted a copy of the full module code to github: https://github.com/jojonaloha/views_custom_filter_example

You should just be able to enable the module to see the filter. If you added the code to an existing module that is already enabled you probably just need to clear caches, or maybe the table didn't exist so Views didn't display the filter? I haven't tested this code specifically in awhile, but I'm pretty sure I was using Views 3 when I wrote the original code.

I realy enjoyed your tutorial, it was great help and i have used the same in my module. I need one little help. Through ur tutorial we can join node to any other table, but can you help me with multiple join. suppose 3 tables node, abc1, abc2. i can currently join node with abc1 and get any field of abc1 as per your tutorial but can i take a field from table abc1 and join it with abc2 table i.e join node -> abc1 -> abc2 and return value from table abc2. please help

Add new comment

Restricted HTML

  • Allowed HTML tags: <a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • You can enable syntax highlighting of source code with the following tags: <code>, <blockcode>, <cpp>, <java>, <php>. The supported tag styles are: <foo>, [foo].
  • Web page addresses and email addresses turn into links automatically.
  • Lines and paragraphs break automatically.
CAPTCHA
This question is for testing whether or not you are a human visitor and to prevent automated spam submissions.

About the Author

Jonathan Jordan, Software Architect