ExpressionEngine and previous/next
Recently ExpressionEngine developer Ryan Masuga complained on Twitter about the lack of ordering when using EE’s next/previous tags. I had actually solved this troublesome issue a while back but never thought of sharing my solution until then. So since sharing this solution with Ryan and receiving his adoration for it I thought I’d share with the rest of the world.
So without further ado I’ll present the code and walk through the various bits and considerations when using this code.
{exp:channel:entries channel="projects" dynamic="off" orderby="entry_date" sort="desc"}
<?php
// Store entries in order
$projects['{entry_id}'] = '{page_uri}';
?>
{/exp:channel:entries}
<?php
$current = '{embed:id}';
$keys = array_keys($projects);
$offset = array_search($current, $keys);
$prev_key = @$keys[$offset - 1];
$next_key = @$keys[$offset + 1];
$prev = ($prev_key) ? '<a href="' . $projects[$prev_key] . '" class="previous">〈 Previous</a>' : "";
$next = ($next_key) ? '<a href="' . $projects[$next_key] . '" class="next">Next 〉</a>' : "";
?>
<div class="side-block tungsten upper prev-next">
<?= $prev ?><?= $next ?>
</div>
So what’s going on here? Let’s explore each section of code.
{exp:channel:entries channel="projects" dynamic="off" orderby="entry_date" sort="desc"}
<?php
// Store entries in order
$projects['{entry_id}'] = '{page_uri}';
?>
{/exp:channel:entries}
This first block sets up a standard {exp:channel:entries}* tag with the proper ordering parameters we want. The difference though is we’re actually storing the output in a PHP array for later use instead of out to the browser. In this case I’m using the page_uri as what I’ll want to use later and the entry_id is the index to my array. In order for this to function properly the template should be set to allow PHP and parse at output.
Next we need to do some magic to calculate which entry is next and which is the previous one to our current entry.
<?php
$current = '{embed:id}';
$keys = array_keys($projects);
$offset = array_search($current, $keys);
$prev_key = @$keys[$offset - 1];
$next_key = @$keys[$offset + 1];
$prev = ($prev_key) ? '<a href="' . $projects[$prev_key] . '" class="previous">〈 Previous</a>' : "";
$next = ($next_key) ? '<a href="' . $projects[$next_key] . '" class="next">Next 〉</a>' : "";
?>
So with this we store our current entry_id for the page/entry from which this embed was requested. Then we find the keys of our previously stored array and calculating the previous and next values.
Once we have those I build a link to the previous as well as the next entry. Which I then output in this code:
<div class="side-block tungsten upper prev-next">
<?= $prev ?><?= $next ?>
</div>
So that’s the magic of the embed itself. In order to make sure it all functions properly you would use an embed tag similar to this:
{embed="template_group/prev-next-template" id="{entry_id}"}
That’s it! Let me know your thoughts or questions in the comments.

Comments
I came across a similar problem a while back but rather than needing next/prev in date order, I needed them alphabetically.
A different approach to yours, but here goes,
{exp:query sql=“SELECT url_title, title FROM exp_weblog_titles WHERE url_title < ‘{segment_3}’ AND weblog_id = ‘5’ AND exp_weblog_titles.status = ‘open’ ORDER BY url_title DESC LIMIT 1”}<p><a href=“url_title” title=“Previous: {title}” class=“prevLink”>Previous</a></p>{/exp:query}
So I used segment_3 to get the URL title of the entry we were on, found the url_title that the db considered ‘less than’ it (previous alphabetically) and away I went.
Just used > for the next entry in another exp:query.
Having seen your solution I could have done the same using an array, probably a lot simpler.
Adam, that’s a slick solution too.
Do we not have to worry about SQL injection if the url segment is used within exp:query?
I have to agree with Adam. Fred’s solution above is interesting but adds a lot of processing overhead. You’re looping through every weblog entry, and bringing in all the extra data/work that comes with exp:channel:entries— selecting over a dozen columns, multiple table joins, loops, etc.
Adam’s solution is a simple select query or two. I’d just add two “where” clauses to it:
AND exp_weblog_titles.entry_date < {current_time format=’%U’}
AND (exp_weblog_titles.entry_date = 0 OR exp_weblog_titles.entry_date > {current_time format=’%U’})
This way, it accounts for entry_date and expiration date. (Change those if you’re using show_expired or show_future_entries).
@Ian— I don’t want to make a full proclamation about this in case I’m wrong, but my understanding is that it’s not an issue, because you can’t use semicolons, apostrophes, slashes, etc. in EE urls without getting an error. Also, I believe the $IN class, which is what {segment_x} uses, applies cleaning for other kinds of dangerous characters. But you should probably confirm.
-Matt
Adam, Ian and Matt,
Indeed a SQL query is a more direct and more efficient method, however it is a more difficult approach to get various orders or searches correct.
The performance overhead of my solution is negligible and I normally use the disable param on the {exp:channel:entries} tag – ex. disable=“categories|member_data|pagination”.
Both approaches are solid and solve the problem but I feel mine is more approachable since you don’t need to be familiar with the inner workings of EE.
Thanks Fred. Absolutely; it’s all about trade-offs. Both work (and are better than the native functionality!).
If you want more than one next or previous entry, say the next and previous five, there are two levels of sorting — first to get the next and previous entries, second to sort their display. My $15 <shameless plug> <a href=“http://engaging.net/docs/nearby-entries”>Nearby Entries</a> plugin handles this </shameless plug>.
Beautiful site, btw.
And another plug for Nearby Entries from me. Adam does great work.
http://engaging.net/docs/nearby-entries
Thanks Fred for giving this topic a home – I’ve actually used Adam Wiggals’s suggested technique, so thanks to Adam too.
If you are following Adam W’s Query solution, to get Next to work, you will need to change DESC to ASC as well as changing the < to a >. Oh, and don’t forget to install the Query module if you haven’t already :)
Can I also second Adam Khan’s comment: this is a beautiful website nGen!