hook_views_query_alter : alter views queries with array keys

01.14.2009

While building a product comparison website, I ran into a situation that required a view to sort using one of 2 possible price totals depending on a users profile: lets say "red state", "blue state". The goal was to open up the product comparison tables (order, fields, field names, and filtering questions and all), as well as the underlying data to this companies' staff, so a homebrew wasn't an option (which it shouldn't be, almost 99% of the time).

Luckily, there were only two possible prices for those products, so all I had to do was create two CCK *decimal* fields [ this is important, because text, and integer fields don't properly store or sort decimal points for prices ]. Then, on hook_cron, or a node submit, I updated the two totals based on other itemized fields.

However, how would the view know which column to sort products by based on a flag stored in the users session? There's about 5 ways to do that, so I went with the 1 minute solution:

Set up two sorts in a single view, and implement hook_views_query_alter(&$view, &$query)

<?php
function product_compare_views_query_alter(&$view, &$query) {
// fyi, anonymous sessions need a record in the user table of 0 to work at all....
 
if ($_SESSION['pref']['state_color'] == 'red') {
   
// we'll have to do the less popular red query
    // which's key is [1] (weighted second in the views sort order interface)
   
unset($query->orderby[0]);
  }
  else {
 
// otherwise, don't do the hill billy logic at all....
   
unset($query->orderby[1]);
  }
}
?>

Discussion

In theory, you could write a custom algorithm that built its own sort queries based on a criteria using this method. You could also probably alter filters, or fields. However, this isn't the only way to have accomplished this -- but its the easiest for me to stomach. Never use this method when existing views features, or view arguments, or even other views hooks may be the proper hammer for this nail.

If I learned anything, its that views a complex beast; flexible like F-22 -- an amazing piece of technology that offers tremendous freedom -- but often that freedom will cause you to crash in the ground if you don't think about what you are doing carefully.

Comments

Wow! A use for query_alter()!

I hate that hook. =)

It's not a bad route, though. I would probably have recommended using an embedded view and doing config magic prior to rendering it in something like hook_view_pre_build() which is a little easier to deal with than query_alter, IMO.

But as you said, there are several ways to do it.

"IMO" - heh -- being the

"IMO" - heh -- being the creator of the module, you definately get to drop the "H".
Views 2 hooks are interesting to me: they feel like they want to morph into nodeapi -- with giant pseudo object arrays, referenced variables, and endless switches on the $op variables and all... (though, I'd never see it going as far as the $teaser, and $page variables). I guess its better to plan for an API (because who can forsee the edge cases that eventually chizzle systems like drupal?), than to build another comment_render().

I played a lot with views while playing with PHP5 reflections, and gotta say... I'm worried our views (especially "frontpage", and "tracker") are going to become self aware, and take over the drupal community for selfish reasons..

Anyways, my favorite part of teaching views when the other persons head explodes -- always the moment that they see the connection between CCK and views, what node types vs taxomony terms vs user ids can mean, and what that means for their websites within menus, blocks, panels, etc.

Unless I'm mistaken, wouldn't

Unless I'm mistaken, wouldn't this run for all views? If so, it would unset the second OrderBy for all other views queries.

To ensure that you affect only your view

I recently modify a views_query_alter. I read somewhere, that's not "supported/recommended" ... but I take this road at my own risk ....
To be sure that you affect only your view, juste add this IF at the begining

if ($view->name=='MY_VIEW_NAME') {

I even modify the where clause by adjusting:

$query->where['0']['args'] ...
$query->where['0']['clauses']['2']....

It works, but this piece of code need to be adjust if the view is modify in the drupal interface.

Happy drupal !

Part of the reason I didn't

Part of the reason I didn't hesitate was that I just was unsetting order bys without having to futz about in one of the massive views array of doom. [not to say that views arrays could be less massive.]

It would actually throw an

It would actually throw an error for unsetting that which isn't set part of the time. But that's what &$view is for... now as to why its passed by reference, i have no clue. Anyways, if you're messing with this hook, you've probabaly heard of dprint_r($view).

Post new comment

The content of this field is kept private and will not be shown publicly.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • You may post code using <code>...</code> (generic) or <?php ... ?> (highlighted PHP) tags.
  • Lines and paragraphs break automatically.
  • Web page addresses and e-mail addresses turn into links automatically.

More information about formatting options

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.