Recently, while helping @lisarex on a project with some views, location & gmap goodness, at some point there were many problems with the taxonomy terms, and led to this one little awesome snippet of code.
There were a long line of taxonomy issues, but most of those were sorted out, or alternative ways found to ignore them. The scenario for the issue is that we had a great view set up that served multiple purposes. The primary page view though, simply rendered a gmap of listings filtered by State, then City using views arguments. There was also an attachment view that was used with the page view to render out summary lists when the 2 arguments were empty, listing the available State/City locations, how many nodes were below that, and allowing quick filtering. The nodes on the map were set to link directly to that listing.
One last piece was needed, and in this case, it was a list of taxonomy terms that were associated with a node referenced to the listing using the node_reference CCK type. Using views relationships, this was easy enough to accomplish, but 2 horrible things became apparent:
- Duplicate terms were created
- Sorting of the terms was "random", based on the order the nodes were presented
If you search for this term on Drupal.org, there are quite a few listings on this similar topic, and all the "geniuses" that actually responded said "Oh, just use a taxonomy view, not a node view". Well, that is WRONG for 99% of the issues that were posted. The need here is to provide a list of the taxonomy terms associated with ONLY the nodes being presented. Not to just dump a full list of terms. This use case I've implemented was in a block view that takes the same arguments as the page view does, which is the state/city pair. The block only appears if both arguments are present, and we are filtered down to our most specific locations
This following code solves both of the above issues, WHILE still using a node view. It implements hook_views_pre_render to manipulate the $view->result data, and give us what we actually expected.
/**
* Implementation of hook_views_pre_render
* This function is in place to filter out duplicate taxonomy terms
* From listings. It will cycle each result, and store a new array of
* unique terms, and when a duplicate is found, will unset that result
*
* @param $view
*/
function your_module_views_pre_render($view){
// first we make sure we are dealing with the correct view
if ($view->name == "your_view_name") {
// create our array for comparisons
$unique_tids = array();
// let's cycle through each default result, and do some dirty stuff
foreach($view->result AS $k => $result){
if(in_array($result->node_node_data_YOUR_FIELD__term_data_tid, $unique_tids) || !$result->node_node_data_YOUR_FIELD__term_data_tid) {
/** we already have seen this TID in the results, so blow that crap away
* also will blow away any that are empty for some odd reason */
unset($view->result[$k]);
}
else {
// this is a term we haven't seen, so let's not blow it away, but add
// it to our array of unique id's to present as the "true" result of this view
$unique_tids[$k] = $result->node_node_data_YOUR_FIELD__term_data_tid;
}
}
/** now, we have an accurate unique list of terms in $unique_tids
* next, we cycle those to reorder the crap random ordering
* since these tids were pulled from the nodes in the order they were
* set to sort from the node type view */
$alpha_arr = array();
// cycle each of our unique tids, referencing the original key of the view->result array ($k)
foreach($unique_tids AS $k => $tid) {
// we need to grab the term now, not the tid to sort alpha
$alpha_arr[$k] = strtolower($view->result[$k]->node_node_data_YOUR_FIELD__term_data_name);
}
// sort the array, maintaining the $k key so that we may again reference back to the original data
asort($alpha_arr);
// create new array of results to overwrite the current one
$new_results = array();
/** cycle one last time now that we have unique terms, sorted alphabetically
the point of this is to now take our $k reference, and grab the original $view->result data
that references this item */
foreach($alpha_arr AS $v => $term_name) {
$new_results[] = $view->result[$v];
}
// get rid of the original result set
unset($view->result);
// replace it with our new, accurate result set
$view->result = $new_results;
}
}
?>
This may or may not be the best solution for this issue, but the code is using the following logic:
- First, grab the $view->result array, and cycle through it, checking for a TID that has not been seen yet, adding it a custom array that has a key of the original key of the specific $view->result item.
- Any items that are already in our array of unique items that is found again, we unset that item in the $view->result array
- After our cycle is done, two more cycles are performed
- First, we have to continue to preserve our $k which references the original $view->result item, and we assign the term name as the value of the array. Once that is complete, we can use asort() to resort this array by alphabetical order, and preserve our key.
- Lastly we loop through the array one last time using our keys to create an array with the original view->result item in the array in the order we have determined was appropriate
- Finally, we unset the original $view->result array, and assign our modified version as what we want to use for this view
And whoosh, the term list view is now only showing unique terms in alphabetical order. </hotsauce>


SWEET
Sweet, this looks to be just what I need!
Post new comment