Using Hook Views Query Alter
Posted on: Friday, September 3rd 2010 by Brandon Tate

Building a website I recently ran into an issue where I had HTML search facets interacting with Drupal views. The user would click on the facet and it would filter the view depending on the argument. I ended up implementing hook_views_query_alter. To make sure the values I wanted to filter on were in the view, I added the search facets into the view as fields. This automatically joins the necessary tables so that you don’t have to.

Implementing hook_views_query_alter requires two parameters, view and query, both are passed by reference. Since this hook is called for every view the first thing I did was check that I was altering the right query by adding an if statement that checks the view name. After that you're free to modify the query as you wish. In my example, I looped through the search facets and added them as where statements and arguments to the query.


function search_views_query_alter(&$view, &$query) {
  if($view->name == 'example_view_name'){
    foreach($parsed_url AS $key => $value){
      $query->where[0]['clauses'][] = 'profile_values_profile_' . $key . '.value IN ("%s")';
      $query->where[0]['args'][] = $value;
    }
  }
}

One of the search facets was a taxonomy filter which the user was allowed to multi select. In order to get the view to grab the correct values, I had to add join clauses to dynamically create term_node table aliases. This is done by adding a join clause to the table_queue and tables array elements within the query object. I was able to just copy the existing term_node table_queue and tables array elements because I only wanted to create different aliases. Then I incremented the existing term_node table count within the tables array. If you wanted to create a new table join dynamically, the same approach is used but the table_queue and tables definitions need to be created by hand.


foreach($parsed_url AS $key => $value){
  $vid = _search_get_vocabulary_id(str_replace('_', ' ', $key));
  $tid = _search_get_term_by_vocabulary($vid, $value);
  $table_term_node_join = $query->table_queue['term_node'];
  $query->table_queue['term_node_' . $count] = $table_term_node_join;
  $query->table_queue['term_node_' . $count]['alias'] = 'term_node_' . $count;
  $query->tables['term_node']['count']++;
  $query->where[0]['args'][] = $tid->tid;
  $query->where[0]['clauses'][] .= 'term_node_' . $count . '.tid = %d';
  $count++;
}

The query table_queue and tables array elements looks like so:

Table Queue:


'table_queue' => array (
  'term_node' => array (
    'table' => 'term_node',
    'num' => 1,
    'alias' => 'term_node',
    'join' => views_join::__set_state(array(
      'definition' => array (
      'left_field' => 'vid',
      'field' => 'vid',
      'table' => 'term_node',
      'left_table' => 'node',
    ),
    'extra_type' => 'AND',
    'table' => 'term_node',
    'left_table' => 'node',
    'left_field' => 'vid',
    'field' => 'vid',
    'type' => 'LEFT',
    'adjusted' => true,
    )
  )

Tables:


'tables' => array (
    'node' => array (
      'node' => array (
      'count' => 1,
      'alias' => 'node',
      ),
    'term_node' => array (
      'count' => 1,
      'alias' => 'term_node',
    )
  ),
);

In my book this makes views more comfortable to use because it gives me more control over them. This along with a view preprocessor allows me to style and output pretty much anything I want within a view.

Comments

wow, so useful........thanks

In merlinofchaos words:

That is Views 1 code, clearly. You might try checking $view->style_plugin but I fear that there is a larger problem here: The general flow of a view is very different now, and using query alter is unnecessary as style plugins in Views 2 have a query() method that they can use to directly modify the query themselves. The style plugin being used there needs to be rewritten.

You can probably just comment out this code for now.

http://drupal.org/node/292514

Post new comment

The content of this field is kept private and will not be shown publicly.
Type the characters you see in this picture. (verify using audio)
Type the characters you see in the picture above; if you can't read them, submit the form and a new image will be generated. Not case sensitive.