Altering A Views Query: Tackling The Node Type Filter Bug

Photo of Greg Harvey
Mon, 2009-10-12 18:53By greg

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 "" option from the resulting UI they will get *everything*


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.

* 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']);
$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 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! =)