Using Views 2 and Drupal 6 to Create a Related Pages Block

Published June 17, 2008

Today's question comes from Dale at NFi Studios in Orlando, FL - my home town:

Essentially:, what i'm trying to do is
1. Determine the current nodes taxonomy terms
2. Determine all other pages that share taxonomy terms
3. Display the title (and link) to those pages in a block

Using Drupal 6.2 and Views - Looked at a few modules, but nothing quite
exact - Reviewing some module snippets right now to see if I can
potentially use an argument to do it.

Dale asks a good question: before the release of Panels for Drupal 6, how can one associate a block with the node it's being displayed next to without writing a custom module? It turned out to be simpler than I expected. I actually started going down the path of custom modules, but in the process I noticed that Views still have the ability to load Arguments with PHP Code, and my solution wrote itself.

Dale, you're already pretty far along: you know you need a view inside of a block, displayed based on taxonomy terms to relate the view to the pages. Instead of giving you a full-fledged tutorial on Views and the Black Arts, you'll be getting a recipe that outlines how to create the block in question.

First, you'll want to download and install Views 2 for Drupal 6, currently in Beta 4, then enable the module under admin/build/modules - remember that Views also requires you to activate Views UI, as the views.module is more of an API for creating views.

The first step in your new Views journey is to Add a new view at admin/build/views. Just click on the tab near the top of the screen:
add a view

Now you'll be asked to name your view something computer-friendly - in this case you'll name it related_by_term:
Name your view

Once you click through, you'll be presented with the granddaddy of all administration screens, but don't be afraid, things move pretty quickly in here if you know what you're doing. Notice the Orange text in the top right: New view. Until you push the Save button at the bottom of the screen, all you hard work could be lost, and especially don't forget to save once you've finished configuring your view.
Main views screen

I want you to skip all those fancy options and dive right in: you've got to eat your vegetables, so let's get the hard part over with and set up the arguments:
Add an argument

Arguments were the most powerful (and hardest to understand) feature of the old Views - think of this as the WHERE clause of your SQL statement. Your first and only argument will be keyed off of a Taxonomy Term ID:
taxonomy argument type

Since you don't have Panels at your disposal, you'll need to specify a Default Argument. There are several (powerful) ways to accomplish this, but in your case, resort to tried-and-true PHP Code. The code displayed here loads the object for the current $node (if any) and concatenates a list of all the Term IDs on that node, then returns the string to Views in the format 1+2+3, just like the arguments on taxonomy/term pages:
use PHP to specify the default argument

In order to activate this muitliple-term-driven argument scheme, you need to check a few boxes below the PHP code. The first sets up the fact that you can use the 1+2+3 format, and the second removes duplicate entries that have more than one term. Make sure you read the warnings about performance; if you don't use multiple taxonomy terms, leave this box unchecked. You can proceed by clicking the Update button:
Allow multiple terms, reduce duplicates

At this point, you'll see an error message inviting you to extract some fields in your view, which is set up to display fields by default. If you wanted to view full nodes or teasers, you could change that option and be safe, but the block you want to build needs titles that link to the node:egads, an error message

Now you want to tackle adding fields to your view. This is the building block of any view, analogous to the SELECT part of a SQL statement:
Add a field

You should recognize this next picture, because it's very similar to the way you chose your argument type, and how you'll choose your filter in the future. Use the Add button to move to the next screen:
you want to see the node title

If you don't delete the word "Title" in the Label text box, it will appear next to all your links. There are some situations where this is wanted; yours is not one of them. You also said you wanted the titles to be links, so check that box:remove the Label and enable links

At this stage, you should be able to get a working preview of your view. Just type a number or a 1+2 in the arguments area and push the button to trigger the AJAX. That blue text is your view, and there's also some geeky information below. Notice that currently "This display has no path"; that's OK, you want a block:
use some arguments to get a live preview

At this point, you still haven't told Views that you want your view to be a block, so make sure you choose block from the drop-down in the main interface. If you're working strictly with Page nodes, you may also want to throw a filter on your view to limit the results. You also should never forget the all-important Save button:filter and save your view

The last thing you need to view is activate your block on admin/build/block (you may need to use block visibility rules, but that's another tutorial), and finally visit one of your Taxonomy-tagged pages to see if your other taxonomy-tagged pages shows up. If you've followed all the steps here, everything should be a snap. Maybe the best feature about your new block is the handy links that pop up when you mouse over your new block (if you have administer views access, at least):
your finished block with edit links

Well Dale, I think that should just about answer your question. Since this is my first Drupal 6 tutorial, I'll have to say, this was a lot more fun for me than I'm sure the last few days have been for you. Wave hello to Corey and Sterling and Derek and Daniel and the other NFi folks, and I hope I was able to save you some precious time with this post.

Comments

one of the few decent drupal tutorials that's both easy to follow and does what it says on the box. THANKS

Submitted by Guest (not verified) on Thu, 09/10/2009 - 11:16

Thanks for posting this tutorial. I suffered the same errors as a few others (incorrect entry of the code) but after a quick browse through the comments and your replies amendments were fast, easy and effective.

The resulting functionality is excellent! kudos to you.

Views has now moved on 6.x-2.6, and unfortunately is quite a bit different to the screen dumps in this article - in particular the arguments screen - any chance of an update?

Submitted by Guest (not verified) on Mon, 09/14/2009 - 17:04

Thank you, Ryan!

The solution to this scenario was puzzling me the last couple of days, building a relatively simple Drupal based site. Couldn't figure out how to do it myself, but now got it working perfectly with your solution. It was the last pending piece of functionality, so I'm really glad someone at drupal.org forums pointed me to this post!

Thanks a great deal for contributing this!

Submitted by Erik (not verified) on Sun, 10/18/2009 - 16:43

Almost the PHP code I was looking for, would it be possible to wish for a similar snipet for "Taxonomy: Term" instead of "Taxonomy: Term ID" ? :-P

Submitted by Guest (not verified) on Sat, 11/21/2009 - 19:48

Hi,

There is one more thing to do with your 'related' view, please:

sorting by numer of common terms. This will make this view more useful in hardly tagged sites.

Thx,
Szy.

First, thank you so much for this. It is of great help :D

Hoping that this is still alive, I would like to ask the following:
Having 4 vocabularies assigned to the node, how can i display relevant nodes that much terms for all 4 vocabularies?

In other words:
Right now, this works like an OR statement. Even if it finds a match for one of the four vocabularies, it will display a node as relevant.
Would it be possible to display relevant nodes with an AND? So that relevant nodes should have one common term from all four vocabularies?

Thanks :D

Submitted by maria_zk (not verified) on Wed, 11/25/2009 - 09:34

For anyone wanting to do a similar thing but with node reference rather than taxonomy, you'd create the argument using content: the node reference field (rather than term id), and then use this code:

$node = node_load(arg(1));
if($node) {
$refnid = $node->node_ref_fieldname[0]['nid'];
return $refnid;
}
return false;

Submitted by Shaun Wolf Wortis (not verified) on Fri, 11/27/2009 - 16:02

found that if use multiple vocabs to node, this trick pulls all of term.
to restrict turned this
foreach($node->taxonomy as $term){$terms[]=$term->tid;}

into this
foreach($node->taxonomy as $term){if ($term->vid=="4"){$terms[]=$term->tid;}}
-----------------------------------------------------------

ps
here we put number of our vocabulary, in my case 4
if ($term->vid=="4")

Submitted by psychoman (not verified) on Wed, 12/02/2009 - 02:36

Any way to sort by the number of commun terms ? showing the nodes that have more commun terms first ?

Submitted by Rida (not verified) on Wed, 12/09/2009 - 10:28

I dig this method for making a related content block via Views - it works, except I've found that when I have the term in related nodes actually displayed as a field in my view powering this block only 1 term will display - the term field renders empty for any nodes tagged with different terms which the live node shares.

Odd.

q./

Thanks for such a good article. I need some help here.
I have two content types.
1. Staff Profile.
2. Staff Video.

Now when one user create staff video content, he also select a staff profile in the Node reference field in Staff Video Content Type.

Now I want to make a view and embed it's link in the Staff Profile content type. SO that whenever any user views any Staff Profile content and click on the Views Link, he should be able to view only that Staff Video where the same name of the Staff profile node was selected. I hope that the picture is clear.
Kindly help me in this regard how am I going to make such a view.

Thanks and regards

Submitted by Guest (not verified) on Thu, 12/17/2009 - 21:31

Great tutorial!

I'd also like to add that this will display the current node in the list... in order to exclude it you would need to add a second Node: Nid argument with the following PHP code:

$node = node_load(arg(1));
if($node){
return $node->nid;
} else {
return null;
}

And select the "Exclude the Argument" box.

Use of PHP inside views is not a good approach because in this case the view is not cached first.
And also this piece of php executed by `eval` function which is not good for performance (afaik).

And even if so, you should not use `node_load`,-who knows which type will it be.
Or which hooks will run on node_load. This might be ve-e-ery heavy.

My suggestion is - if you are already write PHP . Why use views?
Use almost the same php in block.
<?php
$nid = str_replace('node/','',$_GET['q']);
if (!is_numeric($nid)) return; // i only work for nodes
$ns = db_query('select distinct nid,title from {node} n inner join {term_node} tn using (nid) where nid<> %d and tid in (select tid from {term_node} where nid=%d))',$nid,$nid);
$out = array();
while ($n=db_fetch_object($ns)) {
$out[] = l($n->title,'node/'.$n->nid);
}
echo theme('item_list',$out);
?>

something like this.

Good article but still lacks the sorting mechanism. There is no way to dictate that the nodes with highest number of matches should come first.

Submitted by chandrabhan (not verified) on Thu, 01/21/2010 - 02:10

Great Post. I have implemented a view that displays a picture in a block based on the taxonomy of the page with this. However I have a page that is generated by a view. How can I give the page from a view a taxonomy so that my block view can pick it up to display a page?

Submitted by Guest (not verified) on Thu, 01/21/2010 - 02:50

Or you could use the Related Links Module and be done with it. Using Views is a very advanced concept for geeks not everyday users.

Submitted by Guest (not verified) on Mon, 02/01/2010 - 03:18

Thank you Ryan! And, thanks to all the commenters who posted with fixes/updates/adjustments/additional use cases to consider. Stuff like this is why I love Drupal. :)

Submitted by akalata (not verified) on Thu, 02/11/2010 - 21:39

Great tutorial, it worked perfectly.

Any chance of getting the sort by count(common tags) working ..?

The sort order should be:

1 - Drupal, CSS, HTML
2 - CSS, HTML
3 - Drupal

Thanks.

Submitted by TR_Master (not verified) on Wed, 02/17/2010 - 07:21

@TR_Master thanks for your comment. However, this tutorial is simply meant as an example. There are some great modules out there to handle similar situations. Read some of the earlier comments, which refer to numerous solutions.

Submitted by admin on Mon, 02/22/2010 - 10:33

Wish I would have seen this yesterday ;)

Before I found this, I created a similar one using the views group by module where it shows the number of terms in common. The views export is here:

http://kristen.org/content/use-drupal-views-group-module-show-closest-m…

Also, some less views-savvy folks may want to check out the featured content module for easily creating lots of different kinds of featured and related content blocks including sorting by closest match by terms.

http://drupal.org/project/featured_content

Cheers,
Kristen

Thank you - I learned something important from this post!

Submitted by Kevin (not verified) on Fri, 02/26/2010 - 23:30

Great tutorial! I've put it to good use already.

However, I tried to do something similar and failed miserably. Perhaps someone here could help.

I have two content types: 'person' and 'publication'. I want to create a view that shows up on an individual 'person' page, and shows all publications by this person. Both content types contain a field for the first name, one called field_firstname, the other field_pub_firstname. The view should show all the publications where field_pub_firstname == field_firstname (of the current visible single node).

If I add custom php argument code like so:

return 'John';

it correctly shows only the publications by John. However, using what I thought I learned from this page, I tried the following:

return $node->field_firstname[0]['view']

where, in case of John's 'person' page being open, should do the exact same thing.

This doesn't seem to work though. Can anyone tell me why, and what I should do to fix this?

Submitted by Hilko (not verified) on Wed, 03/03/2010 - 12:37

Great tutorial!

If you do this, consider adding a sort criteria for Taxonomy: Term ID descending and put it at the top of your criteria list. That way, newer tags - which are more likely to be *specific* tags - take precedence over more general ones.

For example, I might have an article tagged "Anthrocon" and "conventions". As the "Anthrocon" tag was created later, I get all the stories related to Anthrocon before the ones about other conventions, which is what I want.

Thanks a lot for this step by step guide! How can I change to code to limit this to a specific vocabulary and multiple terms?

Othe steps are clear! Thanks a lot for this guide

James

@sukant

The reason why this happens is because the paths that get reserved by views allow for arguments to be passed.

A workaround for this is to add an argument to the view in question, and then add some validation. One of the options in validating an argument is to return a 404 / Not Found error.

Hope that helps

Submitted by admin on Wed, 05/12/2010 - 14:31

This is fantastic thanks, works a dream.

Submitted by Ronan Keating (not verified) on Fri, 05/21/2010 - 06:43

Hi! The big problem is when you have to negate the term ID argument... how could that be possible??????????

Submitted by negative (not verified) on Fri, 06/04/2010 - 07:40

One nice thing to call out might be the 'Theme: Information' link which tells you which templates the styles will be using, and gives you a full list of names that you can choose from for your own template; and also will show you the default template so you don't even have to go and seek out the original .tpl.php file if you want to look at it.

Fantastic tutorial and comments...what a lifesaver

Submitted by Ann (not verified) on Fri, 06/18/2010 - 08:25

I had a problem using gost_ "What if you want to show only" comment below for my site. The code worked great expect that the block I was using it for was isolated to a single Content Type.

Here is his original code:

$node = node_load(arg(1));
if ($node) {
$terms = taxonomy_node_get_terms_by_vocabulary($node, 6);
foreach ($terms as $tid => $term) {
$tids[] = $tid;
}
return implode ("+", $tids);
}
return false;

Now if I needed this on every page then I wouldn't have had any problem. But I needed it on only a single Content Type page. Now I already had the region isolated to that Content Type, but still it was producing an error. The problem I was having was it was producing this error on every other page that did not have a term.

"warning: implode() [function.implode]: Invalid arguments passed in /home/hpelive/public_html/sites/all/modules/views/plugins/views_plugin_argument_default_php.inc(48) : eval()'d code on line 6."

Now it was discussed below about fixing that by adding a term to each node but that is not really logical if you have many node as some describe.

So my fix was to isolate that in the Views Argument based on Content Type. So I replaced Line 2:

if ($node) {

with this:

if ($node->type == 'episode')

where "episode" is my Content Type. So my entire code looked as follows:


$node = node_load(arg(1));
if ($node->type == 'episode') {
$terms = taxonomy_node_get_terms_by_vocabulary($node, 3);
foreach ($terms as $tid => $term) {
$tids[] = $tid;
}
return implode ("+", $tids);
}
return false;

Not the most complicated of fixes but hopefully it will be able to help someone else.

Submitted by Brad (not verified) on Fri, 06/18/2010 - 19:22

Hi Great tutorial!
How could I build a sort of related terms block using this method on taxonomy/term/x page (not on nodes pages) of "foreign" vocabulary?

Logic something like this:
1.Get the x out of the url (url= taxonomy/term/x)
2. Get the terms out of all the nodes shown on taxonomy/term/x page
3. Show the terms from a specific other vocabulary (not the one from the url).

Would this be doable using above method. What would be the phpcode please?

Thanks a lot in advance for your reply!
Greetings,
Martijn

Thank you very much. Just a question: how can I set the title of the block to something related to the view argument? I would like to set it as "Other nodes in category X". I tried to preprocess the block through template.php but I lost myself in the API. Thanks

Submitted by Leonardo Giordani (not verified) on Mon, 07/26/2010 - 05:13

Leonardo,

When setting up an Argument, there is an area called "Title". Type your text, but instead of "X", put the characters "%1". This is a placeholder for whatever the argument returns. In this case it would be "Other nodes in category Sheep", where you entered %1 in the place of Sheep.

Enter it on this screen, in the Title box.
use PHP to specify the default argument

Sorting problem and already viewed node is displayed again in related content block :(

I'm comming back with the question from few earlier posts:

There is no way to dictate that the nodes with highest number of matches should come first?

I know module like 'similar terms' that make it perfect, but only displays the linked titles to the related node. Is any way to acomplish the same with the views, so I could dislpay other custom fields (ex. image) in the block of related nodes?

Thanks in advance for any helpfull answer.

Hello, I have created something similar as you, but I currently have:
A page (which gets the taxonomy-id from an argument, the taxonomy id represents the category of a product). I've created a relationship to get the seller of the product. But I want next to the product something like:
The seller is ..., they also sell ____ , ____ and ____. So what I'm trying to do here is making an attachment for this (I've managed to get the related products when you put the seller-id as an argument, but I want to get the argument directly from the above-lying page).

I really hope this makes some sense to you.
In a nutshell: How can I pass arguments from the page to an attachment, staying on the same page?

Submitted by Sam (not verified) on Mon, 09/06/2010 - 07:48

Sam,

There is actually a setting in the attachment (at the bottom of the left column while editing the attachment) that allows you to inherit arguments from the main view. This might be off by default, but it's easy to change.

Excellent tutorial, Ryan. You sure helped a lot of people (like me) out with this single post. We owe you countless beers.

Submitted by groomer (not verified) on Fri, 10/08/2010 - 04:13

Hello,

I have successfully followed your instructions in the past, but now I have another problem which is similar but not exactly the same you are talking about:

I want to show related nodes according to user interests (which are content taxonomy fields in a content profile).

That's what I want to achieve:
1. Determine the current user's taxonomy terms
2. Determine all other nodes that share taxonomy terms
3. Display the title (and link) to those pages in a block

That's what I did:
1. Install and configure Content Profile.
2. Create a vocabulary called "interests/targets"
3. Create a taxonomy field called "interests" (linked to vocabulary created before) in a content type using content profile that would be displayed in sign-up process.
4. Create custom content types with a taxonomy field called "target" which would share the same vocabulary ("interests/targets").
5. Create a block using views that would display current's user's content of interests, according to the taxonomy fields.
6. Provided that both content types and users share a content taxonomy field, I wanted to use it as an argument (it's the field called "Interessos" in the attached view) and as default value I chose "logged in user".
7. Since technically users don't have any taxonomy field (I had to use content Profile) I also used that field as a relationship and I made sure that the argument uses the relationship.

Unfortunately it didn't work.

Can you help me, please? Thank you very much

More details are provided in views' issue queue, but unfortunately I didn't get any reply: http://drupal.org/node/923834

Submitted by Carlos (not verified) on Fri, 10/15/2010 - 04:28

Hi,
Thank you for this tutorial !

Is it possible to display the view not in a block of the current page, but on a separate page, on demand (by clicking a button "see related content" for instance) ?

In other words : will the arguments from the current node be automatically transfered when I generate the view on another page ?
Or by extension, how to generate a block with content related to a page on another page ?
That would be awesome !
Many thanks,
Xavier

Submitted by fourmi4x (not verified) on Wed, 11/03/2010 - 05:08

Hello,
anyone tried this with Views 6.x.3-dev? Can´t get it working there.
The SQL for a node with nid = 17 looks like this:

SELECT node.title AS node_title,
node.nid AS nid,
node.language AS node_language
FROM node node
INNER JOIN term_node term_node_value_0 ON node.vid = term_node_value_0.vid AND term_node_value_0.tid = 17
WHERE (node.status = 1) AND (node.type in ('product')) AND (term_node_value_0.tid = 17)

It searches for nodes associated with the term id = 17 while the term associated with the actual node is 253 (17 is the nid as stated above).

Thanks!

Submitted by Hendrik (not verified) on Sun, 01/02/2011 - 02:09

Sign up to receive email notifications of whenever we publish a new blog post or quicktip!

Name
CAPTCHA