Altering A Views Query: Tackling The Node Type Filter Bug

I happened across a wee bug in Views 2 today. I think I've noticed it before, and I'd be a monkey's uncle if it isn't already in the queue, but this is a synopsis:

If you expose the Node Type filter in Views 2 and set it to "Optional", but also with "Limit list to selected items" checked, you might expect it to continue to restrict the results to the selected items it refers to, right? (Well I did.) It does not. If your use selects the "< Any >" option from the resulting UI they will get *everything*

Doh!!

There may be a more elegant solution than this, but here is the code I threw together to get around this issue. It uses a useful Views hook allowing you to massage the SQL query of any View, prior to execution (that coincidentally NikLP, yes everyone, *the* "I never write code" NikLP saved me having to research too hard). It's straightforward enough. It checks if there's a node.type WHERE clause already there, and if there isn't it adds one which selects the content types from my views node type handler.

<?php
/**
* Implementation of hook_views_query_alter().
*/
function economistconferences_features_views_query_alter(&$view, &$query) {
  if (
$view->name == 'newsandreviews') {
   
$type_clause = FALSE;
   
// check to see if we already have a node.type clause
   
foreach ($query->where[0]['clauses'] as $clause) {
      if (
substr(0, 10, $clause) == 'node.type ') {
       
// yes, we do
       
$type_clause = TRUE;
      }
    }
   
   
// if we don't, let's OR our content types together and add a clause
   
if (!$type_clause) {
     
// get types from the view
     
$types = $view->filter['type']->options['value'];
     
// we want an OR query
     
$query->where[1]['type'] = 'OR';
     
// add each type in turn to the clauses and args for this query
     
foreach ($types as $type) {
       
$query->where[1]['clauses'][] = "node.type in ('%s')";
       
$key = key($query->where[1]['clauses']);
       
next($query->where[1]['clauses']);
       
$query->where[1]['args'][$key] = $type;
      }
     
    }
  }
}
?>

Couple of caveats:

1. I've been very lazy. This works for my view, but to be truly generic you would need it to cycle through *all* the available WHERE clauses checking for a node.type clause. Also I didn't ought to hardwire this in to $query->where[1] because one day that might overwrite something. I ought to use $query->where[] and move the cursor, but like I say, I'm feeling lazy and it's late. Just make sure you understand this before you try to use it, or it won't work how you want it to.

2. You will need a Views API implementation in your module and a yourmodule.views.inc file somewhere referred to by the API hook, but all that is for another time (and I know I've blogged it here before).

Two blog posts in one day? I'm exhausted. Time for dinner! =)

Elegant solution

Just add another 'node: type' filter, but don't expose it and use it to restrict the query to the subset of node types you are interested in. Then your exposed filter form will only serve to further filter those.

Less code, more mess, lovely.

Ha!

Now why didn't I think of that?? Nice solution, thanks for sharing. =)

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> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd> <blockquote>
  • Lines and paragraphs break automatically.
  • You may post code using <code>...</code> (generic) or <?php ... ?> (highlighted PHP) tags.

More information about formatting options

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
Image CAPTCHA
Copy the characters (respecting upper/lower case) from the image.