Related nodes view using argument code to check taxonomy terms by vocabulary


corey - Posted on 28 March 2010

A site I was working on recently required that I display a list of related products (nodes) in a view block at the bottom of each page. The same view would have to be used on thousands of different products without needing to change the view code. Also, because of the way the taxonomy needed to be structured (with categories (vocabulary 1) and subcategories (vocabulary 2)) the view would only be checking for related nodes in a single vocabulary.

That's all a bit abstract, here's an example that may be a little more clear:

Imagine a site for a car dealer. This site would have an online catalog with two vocabularies, one with makes and models (Ford, Mustang, Chevy, Corvette, etc) and the other with a body type list (coupe, sedan, 4x4, SUV, etc). The goal is to create product pages for each car, filling out the make and model as well as body type, so that when you view the entry for a Ford Mustang you'll be able to display a view for related cars in two different tabs. The first tab will return other Ford Mustangs, while the second tab returns other coupes.

Here's how I did it:

First, I created my vocabularies. These differed a little from the example above, I actually created a vocabulary called Category and another one called Subcategory. The names aren't terribly important, the two vocabs are independent and can be completely different if you need them to be. After creating the vocabularies, I populated each one with my terms. I chose to set the Category vocab as being a single select list, this would help keep my views as "clean" as possible. Taking the Ford Mustang example above, if the vocabulary were multi-select and someone tagged the Ford Mustang to Chevy for some reason it'd mean having Chevy cars display in the related content box on the Ford Mustang page and vice versa... This way each page is tagged to one main Category, like Ford OR Chevy.

After the vocabularies are set we need to create a few nodes and tag them to the appropriate categories and subcategories. You could do this after your view is created but having a few pieces of content in the system ahead of time will help you troubleshoot the view as you build it.

Now for the fun part, setting up the view. Create a new view, pulling in node data for whichever fields you like. It's not necessary to include the taxonomy term form each result, we'll be pulling that using some php code as the default argument. Sticking with the above example, at this point your view should be returning your selected fields for all nodes you've created, regardless of which Category or Subcategory they are tagged to.

To sort out the nodes that don't share taxonomy terms with the page you're viewing we'll add a default argument to the view. Under the argument section of the view you'll want to select Taxonomy: Term ID and click the radio button to provide a default argument. Next we need to choose PHP code and paste in the following:

$vocab_required = array('2');
$node = node_load(arg(1));

if($node) {
	foreach($node->taxonomy as $term) { 
		if (in_array($term->vid,$vocab_required)) {
			$terms[] = $term->tid; 
		}
		
	}
	return implode('+',$terms);
} 

else { return; }

The code first defines the $vocab_required as vocabulary two, this is my Category vocab, the one that would have makes and models in the example. We then define $node as arg(1), which is the node ID for the page you are viewing. The if statement tells Drupal to pull the taxonomy terms for the node you're viewing, as long as those terms belong to vocabulary number two, and convert those terms into term IDs which are then returned to the view. The view then displays other nodes in the system that are also tagged to those taxonomy terms.

Keeping with the car dealer catalog above, here's how our view would work when viewing the Ford Mustang page. When you view the page the the default argument php script runs and identifies your page by its node ID (pathauto doesn't actually change the url argument of your node so this will still work perfectly if you have your urls aliased). Let's say the Ford Mustang page was the first page you created on your site, so the script now knows it's looking at node/1. The view will now check to see what terms are tagged to node/1 and would see Ford (You may have tagged this to convertible as well but remember, we're only looking in vocab 2 with this view). Moving along, we now convert the term Ford to a term ID and return that to the view. The view then knows to display only nodes that are tagged to Ford.

It's not a terribly long process, we're really only filtering out nodes by their term... but we're doing it dynamically. Remember, this view won't need to be updated later as you start adding other nodes for other terms in vocabulary 1. So when you get to the Pagani Zonda page you won't need to create a new view that only returns nodes tagged to Pagani.

With the use of the quicktabs module we can easily create a block with tabs to display our view and any other views you'd like. You may, for example, want to create a duplicate version of this view and change the required vocabulary variable so you can display all convertibles, coupes, or trucks on a second tab.

Have any questions or suggestions on improving this walkthrough? Post them below.

..

Many thanks for posting this. It's a great help.

I'm getting the following error message when the taxonomy in question isn't filled in (it's not a required field in my setup):

warning: implode() [function.implode]: Invalid arguments passed in C:\ApacheRoot\drupal_dev\sites\all\modules\views\plugins\views_plugin_argument_default_php.inc(48) : eval()'d code on line 11.

Do you have any suggestions on how to fix this?

OP here. Moving the implode up into the if statement seems to fix it.

$vocab_required = array('2');
$node = node_load(arg(1));

if ($node) {
foreach($node->taxonomy as $term) {
if (in_array($term->vid,$vocab_required)) {
$terms[] = $term->tid;
return implode('+',$terms);
}

}

}

else { return; }

It worked well for me. To exclude current page from view, I used this thoughts - http://drupal.org/node/131482