Drupal: "previous and next" links in each node?

12,276

Solution 1

The Custom Pager module does exactly what you want. You can use a view to define which nodes are the next/previous one.

If you're using the Image module, the setup of the custom pager for images is in the documentation.

Solution 2

Here's a D7 version of aterchin's code.

<?php
/**
* Previous / Next function for nodes, ordered by node creation date
*
* @param $current_node: node object or node id
* @param $node_types:  array of node types to query
*
* @return array
* 
*/
function MODULE_prev_next_node($current_node = NULL, $node_types = array()) {
    // make node object if only node id given
    if (!is_object($current_node)) { $current_node = node_load($current_node->nid); }

    // make an array if string value was given
    if (!is_array($node_types)) { $node_types = array($node_types); }

    // previous
    $prev = db_select('node', 'n')
    ->fields('n',array('nid','title','created'))
    ->condition('n.status', 1,'=')
    ->condition('n.type', $node_types,'IN')
    ->condition('n.created', $current_node->created,'<')
    ->orderBy('created','DESC')
    ->range(0,1)
    ->execute()
    ->fetchAssoc();

    // next or false if none
    $next = db_select('node', 'n')
    ->fields('n',array('nid','title','created'))
    ->condition('n.status', 1,'=')
    ->condition('n.type', $node_types,'IN')
    ->condition('n.created', $current_node->created,'>')
    ->orderBy('created','ASC')
    ->range(0,1)
    ->execute()
    ->fetchAssoc();

    return array('prev' => $prev, 'next' => $next);
}
?>

Solution 3

Using Drupal 8, I ended up creating a twig extension located in modules/MY_MODULE/src/TwigExtension) (where MODULE_NAME is your module machine name, if you don't know how to create a custom D8 module, you can refer to this official post)

namespace Drupal\MODULE_NAME\TwigExtension;

use Drupal\node\Entity\Node;

class PrevNextNode extends \Twig_Extension {
    /**
     * Generates a list of all Twig filters that this extension defines.
     */
    public function getFunctions() {
        return [
            new \Twig_SimpleFunction('customPrevious', array($this, 'customPrevious')),
            new \Twig_SimpleFunction('customNext', array($this, 'customNext'))
        ];
    }

    /**
     * Gets a unique identifier for this Twig extension.
     */
    public function getName() {
        return 'custom.prevnextnode_extension';
    }

    /**
     * Previous node
     * @param $prevNextInfo
     * @return array|bool
     */
    public function customPrevious($prevNextInfo)
    {
        $node = Node::load($prevNextInfo['nid']);
        return $this->getNodeInformation($prevNextInfo['node_type'], $node->getCreatedTime(), '<', 'DESC');
    }

    /**
     * Next node
     * @param $prevNextInfo
     * @return array|bool
     */
    public function customNext($prevNextInfo)
    {
        $node = Node::load($prevNextInfo['nid']);
        return $this->getNodeInformation($prevNextInfo['node_type'], $node->getCreatedTime(), '>', 'ASC');
    }

    /**
     * Get current langcode
     * @return string
     */
    public function getCurrentLangcode()
    {
        return \Drupal::languageManager()->getCurrentLanguage()->getId();
    }

     /**
      * Previous or next node
      * @param $node_type
      * @param $date
      * @param $date_comparator
      * @param $sort_order
      * @return array|bool
      */
    public function getNodeInformation($node_type, $date, $date_comparator, $sort_order)
    {
        $prev_or_next = \Drupal::entityQuery('node')
            ->condition('type', $node_type)
            ->condition('status', 1)
            ->condition('created', $date, $date_comparator)
            ->sort('created', $sort_order)
            ->range(0, 1)
            ->execute()
        ;

        if(!$prev_or_next) return false;

        // Get the node itself
        $prev_or_next = Node::load(array_values($prev_or_next)[0]);
        // Get the available languages for the given node
        $available_languages = $prev_or_next->getTranslationLanguages();
        // If the current language is defined in the available languages array
        if(array_key_exists($this->getCurrentLangcode(), $available_languages)){
            // Get the translated node
            $translation = $prev_or_next->getTranslation($this->getCurrentLangcode());

            // Return the information you need, can be w/e you want.
            return [
                'title' => $translation->getTitle(),
                'id' => $translation->id(),
                'path' => $translation->toUrl()->toString()
            ];
        }

        return false;
    }
}

Then you'll have to register your twig extension as a service (MY_MODULE.services.yml located in modules/MY_MODULE).

services:
  # Next / Previous links on selected node pages. class: namespace of your extension
  custom.prevnextnode_extension:
    class: Drupal\MODULE_NAME\TwigExtension\PrevNextNode
    tags:
      - { name: twig.extension }

Clear the cache of your Drupal 8 and you'll be able to use your new twig extensions wherever your want in your HTML.

{# make sure you get 'content_type' and 'nid' parameters (a preprocess file would be a good start) #}
{%
    set prevNextInfo = { 'node_type': 'content_type', 'nid' : 10 }
%}

{% if prevNextInfo.node_type and prevNextInfo.nid and (customPrevious(prevNextInfo) or customNext(prevNextInfo)) %}
    <div id="node-pagination">
        {% if customPrevious(prevNextInfo) %}
            <a href="{{ customPrevious(prevNextInfo).path }}" class="pagination-btn pagination-prev">{{ 'Previous node' }} : {{ customPrevious(prevNextInfo).title }}</a>
        {% endif %}
        {% if customNext(prevNextInfo) %}
            <a href="{{ customNext(prevNextInfo).path }}" class="pagination-btn pagination-prev">{{ 'Next node' }} : {{ customNext(prevNextInfo).title }}</a>
        {% endif %}
    </div>
{% endif %}

Solution 4

I think the Previous/Next API module may be what you're looking for.

From the module page:

An API for browsing next/previous nodes without overloading your database server.

This module allows you to know the previous or next nodes for any given node. This is very useful for providing navigational links to the user without the expensive queries required to dynamically deduce such information on the fly.

Solution 5

other way: "book" module in drupal package.

Share:
12,276
aneuryzm
Author by

aneuryzm

Updated on June 20, 2022

Comments

  • aneuryzm
    aneuryzm almost 2 years

    I would like to add previous and next buttons to each node of my Drupal website.

    These links are supposed to point to the next node in the website content.

    How can I make it ? Thanks

  • Arvid
    Arvid about 8 years
    This is a neat solution since some of the mentioned 3rd party modules in other answers blow things up a bit. But I think there's a minor error in your function's first line which is easy to fix though: If the !is_object(…) check is true then you shouldn't access the $current_node->nid field but use the $current_node value directly. In addition you might also change the check to something like is_numeric(…) or even is_int(…).
  • Scott Anderson
    Scott Anderson over 7 years
    Thanks this was a really helpful start.