Community Documentation

Creating View of Distinct Active Taxonomy Terms (browse by category)

Last updated February 4, 2011. Created by RedTop on April 14, 2010.
Log in to edit this page.

1. Introduction

1.1 Background

A list of distinct active taxonomy terms can be useful as it allows visitors to browse content by taxonomy term (categories). Trying to set up such a list using views often results in a list with duplicates. As lots of people seem to hit the same wall when trying to set this up I thought I'd put in the effort of creating this recipe. :)

1.2 Requirements

For this you'll need to have views installed and taxonomy enabled. You should already know how to set up vocabularies and how to categorize content. If not, you may want to read the Taxonomy handbook first.

1.3 Update history

When I first created this recipe I used a method provided by Himerus on his website. After a couple of months a much, much easier way of doing this became available thanks to joel_guesclin and merlinofchaos.

2. Solutions

2.1 Solution using Term View

This solution is easier than the original (advanced solution) recipe found below while still offering the required flexibility to group terms etc. It uses Term View and not Node View thus preventing the duplicates issue.

1) add a new view of view type "term" (not the default "node" option!) by ticking the correct option after clicking 'add view'
2) add the Taxonomy Term to the list of Fields
3) Add a relationship "Taxonomy: Node" and make it obligatory
4) In the basic settings enable the 'distinct' option.
5) Add some filters to narrow the results as needed. (i.e. only nodes from a certain content type)

That's it! This should solve the enormous amount of issues with duplicates. :)

2.2 Original recipe

Below is the original, more difficult and probably less efficient recipe. I kept this for archiving purposes. Maybe it is still useful to some although I do recommend the method described above.

2.2.1 Basic setup

Regardless of whether you go for the basic or advanced solution, make sure you have done the following:

1) add a new view of view type "node"
2) set row style to "Fields"
3) optionally set style to "Grid"
4) though not strictly necessary lots of you will want to add filters to limit the content types and limit to published content.
5) add a field "Taxonomy: Term"

Now, usually the resulting list contains duplicates as the view simply lists the terms of every node that passes the filtercriteria. So if there are two nodes that pass the filter and both have taxonomy term "little green birds", the list would contain two instances of "little green birds".

There's a basic solution and a more advanced solution:

2.2.2 Basic solution

The basic solution, thanks to drupal3008 uses the views "Arguments" section:
1) add an argument for "Taxonomy: Term"
2) Title: %1
3) Action to take: Summary, sorted Ascending
4) Wildcard: All
5) Wildcard Title: All
6) Basic Validation
7) Hide view / Page...
8) Case: Capitalize each word (This is my preference)
9) Case in path: Lower case
10) Transform spaces to dashes (checked)
11) On next option after you add field, select: List

This presents you with a basic list of all active taxonomy terms. However, it strips the possibility to order the terms according to vocabulary and allows little control over the output.

2.2.3 Advanced solution

The advanced solution involves creating a custom module for using hook_views_pre_render:

custom.info

; $Id:

name = "Custom"
description = "This module contains modifications to modules so that the modules themselves can be easily upgraded without redoing the modifications."
package = "Administration"
core = 6.x

custom.module, copied from himerus' website

<?php
// $Id$

/**
* 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;
}
}
?>

1) ... remember to delete the PHP closure tag (get rid of ?>) to comply with Drupal coding standards. It's just used here to enable syntax highlighting.
2) replace"your_view_name" in line 14 to, well, your view name.
3) replace YOUR_FIELD with your field. This can be found by hovering over the field "Taxonomy: Term" we just added and looking at the url which appears in the browsers statusbar. The field name is behind the last slash ("/"). Make sure to keep the double underscore after YOUR_FIELD. It's not a typo.
4) enable the module.

Now, this code may or may not not work straight away. If it doesn't it probably has to do with the naming pattern (i.e. "node_node_data_YOUR_FIELD__term_data_name" and "node_node_data_YOUR_FIELD__term_data_tid" should be different)

To find out what the correct naming pattern is insert this just before line 14:

drupal_set_message('<pre>'. print_r($view, TRUE)  .'</pre>');

Run the live preview of the view (On the edit view form) after which there should be a drupal message showing the contents of array $view.

Look for something like:

[0] => stdClass Object
(
  [nid] => 249
  [term_data_tid] =>
  [term_data_name] =>
)

As you can see, in this case "node_node_data_YOUR_FIELD__term_data_name" has to be replaced with "term_data_name" and "node_node_data_YOUR_FIELD__term_data_tid" has to be replaced with "term_data_tid". This is just an example, but should get you going.

Save the lot and you should have a list of distinct, active taxonomy terms. If so, you can start editing the view to your heart's content. This is really the biggest advantage of the more advanced solution. For example, you could group the list by vocabulary if you are using more than one:

1) add a field "Taxonomy: Vocabulary Name"
2) Edit the field to exclude the field from display as we don't want to display this field, we just want to use the data.
3) optionally, rewrite the output of the field to something like: "Browse by [FIELD]", changing FIELD to the fieldname. You can find the fieldname by hovering over the Taxonomy: Vocabulary name" field. Usually it's "name_1".
4) Edit the settings of the view "Style" (click on the cog behind "Style").
4) select Grouping field: "Taxonomy: Vocabulary name"

Comments

I tried this to the letter

I tried this to the letter and it just didnt do the trick for me =/ When ever i enter

drupal_set_message(''. print_r($view, TRUE) .''); at line 14 nothing happends either?

I have been struggeling with this views duplicate thing for ages now so help would be greatly appreciated.

Sure would like to help...

... but I'll need more information than that. :)

What did you do to debug this?
Just to make sure:
- did you create a custom module?
- did you change your view name?
- did you change the field names?

My best guess is that your code doesn't use the correct fieldnames (which is what happened to me as well. Took quite a bit of debugging and help). Make sure you use the exact code I posted above:

drupal_set_message('<pre>'. print_r($view, TRUE) .'</pre>');

(I tried again before posting this reply to check that it works).

Enter it on the line after:

if ($view->name == "your_view_name") {

Attach your code in a file if it still doesn't work. :)

Hey thanks il give this a

Hey thanks il give this a try. I ended up just creating a term view that displays all my terms irrespective of whether or not they contain content which is not idea but at least i dont get duplicate items anymore. I am going to give your suggestions a try asap because i honestly dont think my way of doing it is close to ideal haha. Thanks for the help =]

Solution without coding

Hello,

This solution worked for me and it doesn't need any coding in PHP.

Add taxonomy term as a filter
Select: is one of and select all the options.
check: Reduce duplicates

Speed

Thanks a lot for this – I tried the "easy" version and it worked great. I wonder if anyone knows what the fastest solution is? I suspect the "advanced" solution could get slow from all that looping and calls to in_array, whereas the simple one narrows it down to one database call.. However, this call has "DISTINCT" in it, which I think is bad for performance?

Great

This is great I have been coding all day just finished figuring out zip code proximity search and this made browse by taxonomy soooo much easier!
I have a lost... now I'll figure out what to do with my list :)

Ron Sparks - Learning a lot about Drupal!
person: www.stratospheredesign.com
blog: www.enhanceux.com

Term links broken

Hi,

I used the basic solution and created the list using views as instructed, I am able to display the taxonomy terms with the node count but the links to their taxonomy pages are broken, what could i have done to cause this.

Please help.

Thanks,
Manoj

Same here.. I get a page

Same here.. I get a page which says January 1970..

The links of the terms are : blog/term

and the terms attached to the nodes read : category/category/term

so somethings a bit off ?

THANKS A LOT!!!

THANK YOU!!
I tried two days to found solutions about duplicates in taxonomy terms and... this is it!
THANK YOU A LOT!!!

Much easier solution

Unless I've misunderstood, this is exactly what I was trying to do. In fact I wanted to have a block showing tags from a particular vocabulary, with nodes attached that were created during the last 2 months. And indeed I had duplicates in the terms because the DISTINCT was applied to nodes not terms.
The explanation (when it was explained to me) is very simple: DISTINCT is applied depending on query type, so the mistake I was making was to have a Node View. Here is what I did:
1) Create a new Term View (not a Node View like I had before and like the example here)
2) Add the Taxonomy Term to the list of Fields
3) Add a relationship "Taxonomy: Node" and make it obligatory
4) Now the Filters allow me to filter on Node Post Date ("now -2 months")
5) Add another filter to get just the one vocabulary that I want
Hey presto! It works! Thank you Merlin!

That's fantastic! I tried and

That's fantastic! I tried and it works beautifully... I will update this recipe asap. :)

Joel, this was easy and

Joel, this was easy and worked. thanks.

good recipe. is there a way

good recipe.
is there a way to display the number of nodes that are in that particular term?
like Term 1 (3), Term 2 (1) Term 3 (0), etc...

I tried your "easier" idea

I tried your "easier" idea however when I tested it to see if it points to the correct page I found that it is directing me to sitename.com/term instead of sitename.com/taxonomy/term/$tid . how do i fix this?

how hide a field's view by code

I cannot found a solution;
which is the php code for to hide a field's view?

I read that perhaps the hook is hook_views_pre_render,
but I only found code for hide rows;

any idea for hide fields?

Great post. Solve the problem perfectly. A big thanks to RedTop for the post here and Jake Strawn (aka: himerus) for the original solution.

For my application, I needed to retain the weight of the nodes as set by the administrator and thought I'd share that in case someone else was faced with the same requirement.

To use weighted ordering rather than alphabetic ordering, simply replace these lines:

<?php
 
...
  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);
  ...
?>

with:

<?php
 
...
  foreach(
$unique_tids AS $k => $tid) {
   
// Fetch the term so we can access its weight
   
$term = taxonomy_get_term($tid);
   
// Use the weight as the array index
   
$alpha_arr[$term->weight] = strtolower($view->result[$k]->node_node_data_YOUR_FIELD__term_data_name);
  }
  ...
?>

and change the final foreach() loop to:

<?php
 
...
  foreach(
$alpha_arr AS $v => $term_name) {
   
$result = $view->result[$v];
    if (
$result->term_data_tid) {
     
$new_results[] = $result;
    }
  }
  ...
?>

Optionally change the variable name $alpha_arr with $weighted_arr throughout. =)

Hi mkoistinen, it didn't work

Hi mkoistinen,

it didn't work for me, Parse error: syntax error, unexpected $end in /var/www/sites/all/modules/custom/custom.module on line 58

<?php
// $Id$

/**
* 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 == "events_views_taxonomy") {
 
// 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_name__term_data_tid, $unique_tids) || !$result->node_node$
   
/* 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_name__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) {
   
// Fetch the term so we can access its weight
   
$term = taxonomy_get_term($tid);
   
// Use the weight as the array index
   
$alpha_arr[$term->weight] = strtolower($view->result[$k]->node_node_data_name__term_data_nam$
  }

 
// 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 d$
   *    that references this item
   */
 
foreach($alpha_arr AS $v => $term_name) {

   
$result = $view->result[$v];
    if (
$result->term_data_tid) {
   
$new_results[] = $result;
}
}
?>

Help please!

tnx

Other solution

Hi,

I was also struggeling with creating a distinct view of active taxonomy terms, needed it in combination with taxonomy image and only needed to see the first 8....maybe it's a stupid suggestion but what i did was the following:

1. Created a content type corresponding with the taxomy term
2. Added a cck field with true and false option and set it for one node of each taxonomy term
3. Created a view with the fields taxonomy:term and in my case taxonomy: Term image
4. Added a filter to view taxonomy term and an extra filter to check if the cck field of the content type is true

Kinda bussy right now, hope you understand it :)

Cheers
Henk

saved me a lot of time

great, thanks for that, i used your first suggestion, (except i used a node view as term view as suggested didnt work):


1) create a new Term View (not a Node View like most people tend to use and like the previous method below)
2) add the Taxonomy Term to the list of Fields
3) Add a relationship "Taxonomy: Node" and make it obligatory
4) Add some filters to narrow the results as needed. (i.e. only nodes from a certain content type)

first way documented does not work for me

Just wanted to let you know that I've tried the first way documented and checked it twice, I still get duplicates. Am about to try the original way. Just wanted to record this here. I think, because of how this recipe has been updated over time there is some ambiguity about which method the comments are referring to. Just to be clear, the method I am referring, is as follows:

After a couple of months a much, much easier way of doing this became available thanks to joel_guesclin and merlinofchaos:
1) create a new Term View (not a Node View like most people tend to use and like the previous method below)
2) add the Taxonomy Term to the list of Fields
3) Add a relationship "Taxonomy: Node" and make it obligatory
4) Add some filters to narrow the results as needed. (i.e. only nodes from a certain content type)

Ellie.

P.S. Also wanted to say (despite the problems I'm having), thank you for putting this together. This has been causing me some heartache!

Update 25 January 2011
Oops! Just realised my mistake... should have seen it amongst other comments. I had to find the distinct section in the basic settings section. I hadn't noticed this option before so I didn't know to look for it. Setting that Distinct option to yes worked for me.

updated text

Hi Ellie,

I have added headers to the text. I agree the text had become harder to read over the months.

Which solution are you using? I understand you have opted to use the (newer) Term View solution and had to tick the 'use distinct' option. Is that correct? If so I will add it to the recipe.

How to Display Counts in Taxonomy Term View

I have created a view to Browse the content by Ctaegory. I want to display Count along with term. How to do that.
Thanks in advance

Hierarchical customizable taxonomy list

Hi

I am having issues with building a heirarchical taxonomy menu/list with views.

I have managed to build one with the "taxonomy module", but it does not provide any options to customize it, like specially I want the menu to be collapsible.

I have also managed to build one with the views and editing the views theme template file template file, Again stuck at furthur disigning it to make it collpsible.

Suppose the menu is something like this :-

+ Music

  1. Istrumental
  2. Rock

+ Test

  1. Test1
  2. Test2

When the "+" beside the "Test" option is clicked then the display should be something like this :-

+ Music

  1. Istrumental
  2. Rock

- Test

Can this be achevable? Waiting for all your guidance/suggestions.

Thanking you all,
Pradeep.

Got by the help of nice menus

Hi to everyone,

I was able to acheive the above mentioned requirement after hard research and found the "nice menu" module, this did not exactly met my requirement but this was something similar to which I wanted the display of the taxonomy listing.

how to do this in D7?

these solutions don't work for me in D7. when I follow the simple solution I get all my second level terms but not the first level. So I'm able to get all terms or only second level terms but not first level terms.

emk

I solved duplicates situation

I solved duplicates situation with 2 taxonomy vocabulares, and 2 terms from different vocabulares assigned to node.
In my node fields settings I created cck field of type "Conent taxonomy" with reference to first vocabulary. Second vocabulary is assigned via standard way. Then i created a view, and added in filters option: Content: content taxonomy field (attention, not a standard Taxonomy options like "Taxonomy: Term" and others).

It works!

-------
CEO of http://arbuzcube.com - drupal themes

thank you

used the term view solution and was able to apply it to the slidedeck view to create exactly what i wanted. racked my brain all day, and your solution only took 2 minutes. you rule, thanks again.

Page status

No known problems

Log in to edit this page

About this page

Drupal version
Drupal 6.x
Audience
Developers and coders, Site administrators

Site Building Guide

Drupal’s online documentation is © 2000-2012 by the individual contributors and can be used in accordance with the Creative Commons License, Attribution-ShareAlike 2.0. PHP code is distributed under the GNU General Public License.
nobody click here