Adding Pager Navigation to Nodes in Drupal 7
My father, a part time sculptor, recently asked me to build him a website to showcase his work. Sensing an opportunity to learn some new Drupal 7 tricks, I agreed.
Being a fairly simple site with a few pages and an image gallery, the site was completed in a few days…almost. When I was at a stopping point, I asked my wife to click through the site, thinking she might pick up on some usability issues that I missed. Sure enough, she did.
She noticed that after clicking on a thumbnail in the gallery (a simple grid view of thumbnails) and arriving at a sculpture node, there was no way to get to the next sculpture without going back to the gallery. “I want to keep clicking through to the next sculpture, and the next one after that,” she said, “but I can’t.” She was absolutely right. This was a huge usability flaw in the site.
Next and Previous buttons were the obvious solution- but not so obvious to implement. In Drupal’s architecture, nodes are discrete bundles of data. A node doesn’t have any concept which other nodes are next or previous to itself the way a double linked list does.

For websites built around image galleries, node-level pagers make perfect sense.
With some Googling, I found some prebuilt solutions: the Custom Pager Module and the Previous/Next API. Unfortunately, at the time of this writing, neither was available yet for Drupal 7. With no other options, I set out to build them myself.
Using some examples I found here and here and modifying it to work with Drupal 7, I came up with the following theme function for my template.php file:
<?php
function dad_prev_next($current_node = NULL, $op = 'p') {
if (
$op == 'p') {
$sql_op = '<';
$order = 'DESC';
}
elseif ($op == 'n') {
$sql_op = '>';
$order = 'ASC';
}
else {
return NULL;
}
$output = NULL;
$sql = "SELECT n.nid, n.title
FROM {node} n
WHERE node $sql_op :nid
AND type IN ("sculpture")
AND status = 1
ORDER BY nid $order
LIMIT 1";
$result = db_query($sql, array(':nid' => $current_node->nid));
foreach ($result as $data) {
}
if (isset($data)) {
if ($op == 'n')
return l("Next", "node/$data->nid", array('html' => TRUE));
else if ($op == 'p')
return l("Previous", "node/$data->nid", array('html' => TRUE));
}
}
?>Then, in my node.tpl.php template file, I added:
<?php
print dad_prev_next($node,'p') . " / " . dad_prev_next($node,'n');
?>Once I had it working, I tried getting a little fancier to hide the Previous button on the first node and hide the Next button on the last node:
<?php
if (!$prev==NULL && !$next==NULL)
print dads_prev_next($node,'p') . " / " . dad_prev_next($node,'n');
else
print dad_prev_next($node,'p') . dad_prev_next($node,'n');
?>That’s it. In the example I’ve provided, nodes are paged through in order of Node ID. But you can page through in any order you choose by modifying the SQL query. In fact, after I implemented the Next and Previous button in this project, I added an interface using the awesome Draggable Views module so my father could change the order that his sculptures are displayed in the gallery view. Since it would only make sense if the Next/Previous buttons paged through the sculpture nodes in the same order they’re displayed in in the gallery, I had to modify the SQL query in my Next/Previous template function accordingly. If anyone is interested, I will provide this code in the comments.
Happy paging!
10 comments
Error?
I get this error:
PDOException: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'node' in 'where clause': SELECT n.nid, n.title FROM {node} n WHERE node < :nid AND type IN (practice) AND status = 1 ORDER BY nid DESC LIMIT 1; Array ( [:nid] => 11 ) in rogerloos_prev_next() (line 170 of C:\xampp\htdocs\drupal7\sites\all\themes\techedge\template.php).
How to fix it?
Drupal 7 Pagination / Pager within Node, example
@ Davide: The error message tells you already what's wrong. "Unknown column 'node' in 'where clause'" -> code: WHERE node $sql_op :nid
How to fix this:
Take a look on the "node" table within your drupal database and (not) surprisingly you won't find any "node" column in there. That's why the query fails.
The where clause makes a simple comparison, either "greather than" (>), or "less than" (<) :nid
:nid == the current node id
You have to replace node with nid, then it will work right. (The WHERE clause looks for nodes which have an id thats greater / less than the current id...)
It's fixed here:
$sql = "SELECT n.nid, n.title
FROM {node} n
WHERE n.nid $sql_op :nid
AND type IN ('replace-with-content-type')
AND status = 1
ORDER BY nid $order
LIMIT 1";
Have fun.
Adding a title?
Hi Chris - got this working easily once I read the comments... nice one!
I would like to alter it so that it works more like this page: http://www.theverge.com/2011/11/8/2518608/walking-as-jazz-virginia-tech-...
You can see at the top of the page they have basically do what you have done here but instead of previous/next they have added the page title. I am not php good enough to figure out how to get that into an array and then out into a print - would it take much to alter your code to do this?!
It's for deployment on a largish site so I will probaly bump the output into a php block and include the output only on the required content type.
Cheers
Allen
Thanks for this, been stuck
Thanks for this, been stuck for two days until I came across your solution. Worked perfectly!! (I had to make the change in the previous comment too.)
Cheers
-gray
another error and draggable views
Thanks for sharing this, Chris. I am not a developer and I have been unable to get this to work.
When I drop in the function in the template file, I get the error:
Parse error: syntax error, unexpected T_STRING in /home/mysite/drupal-7.10/sites/all/themes/mythemename/template.php on line 34
This is the function in my template file (I also removed the ending php tag as I understand this is Drupal protocol):
function dad_prev_next($current_node = NULL, $op = 'p') {
if (
$op == 'p') {
$sql_op = '<';
$order = 'DESC';
}
elseif ($op == 'n') {
$sql_op = '>';
$order = 'ASC';
}
else {
return NULL;
}
$output = NULL;
$sql = "SELECT n.nid, n.title
FROM {node} n
WHERE n.nid $sql_op :nid
AND type IN ("gallery")
AND status = 1
ORDER BY nid $order
LIMIT 1";
$result = db_query($sql, array(':nid' => $current_node->nid));
foreach ($result as $data) {
}
if (isset($data)) {
if ($op == 'n')
return l("Next", "node/$data->nid", array('html' => TRUE));
else if ($op == 'p')
return l("Previous", "node/$data->nid", array('html' => TRUE));
}
}
Line 34 is the: AND type IN ("gallery") line, but I have a content type with a machine readable name called "gallery". I can't see why there is a problem.
Also, I would like to to know how you integrated draggable views and sorted differently than the nid. I cannot use nid and this issue is why Flippy (7.x-1.x-dev) doesn't work for me. I would like the prev/next links to correspond to the view order.
Also, I understand I should be using preprocess over template.php where possible. Would this be a scenario to use it? I'm using the Omega theme and trying to figure out how to use preprocess.
Thanks Chris!
Thanks Chris!
A very lucid, well written blog-post!
I haven't tried the software yet, but I am going to give it a try right now.
That sculpture looks beautiful!
Thanks :-)
This was very useful, I was stuck with this for ages. I have a few things though:
How did you manage to do it in a different order than just the nodes?
And also, I have a couple of galleries of images on a site I'm building, the images have a type "gallery" that is used in the type, and this is used to show only these ones in the views, but I want the next/previous to only go through the images in each gallery. From what you did, it goes through all of the 1 type. I spent ages going through the mysql to try and figure out how to use the "gallery" field in the image type that I have, but I can't figure it out anywhere. Any ideas?
Thanks again for this, its awesome.
I'd appreciate the draggable
I'd appreciate the draggable views integration if that's possible.
Worked perfect! thx!
Worked perfect! thx!
Gracias!!
Gracias!!
Post new comment