How to use next_post_link and previous_post_link on single posts in search results

I’m using next_post_link and previous_post_link on my single post templates, to navigate from post to post, so far so good.

But if I do a search and click on a result post, then the next_post_link doesn’t bring me to the next result post, but to the next post in the default post order.

Read More

Is there a way to make next_post_link and previous_post_link behave in different ways depending on the context ? When in a search context, browse only within search results, etc.

EDIT : I’m looking for a solution that would work with custom post types.

Related posts

Leave a Reply

2 comments

  1. Building on Bainternet’s answer above, but making it more generic, I wrote this quick plugin. You can probably modify the link building function at the bottom to do what you want more exactly.

    <?php 
    /* 
    Plugin Name: Search Context
    Description: Use search context on single post pages when they're reached from a search results page to adjust the prev/next post links.
    Author: Otto
    */
    
    add_action('init','search_context_setup');
    function search_context_setup() {
        global $wp;
        $wp->add_query_var('sq');
    
        add_filter('previous_post_link','search_context_previous_post_link');
        add_filter('next_post_link','search_context_next_post_link');
    
        if (is_search()) {
            add_filter('post_link','search_context_add_search_context');
        }   
    }
    
    function search_context_add_search_context($link) {
        $sq = get_search_query();
        if ( !empty( $sq ) )
            $link = add_query_arg( array( 'sq' => $sq ), $link );
        return $link;
    }
    
    function search_context_previous_post_link($link) {
        $sq = get_query_var('sq');
        if (empty($sq)) return $link;
    
        return get_search_context_adjacent_link($link, $sq, true);
    }
    
    function search_context_next_post_link($link) {
        $sq = get_query_var('sq');
        if (empty($sq)) return $link;
        return get_search_context_adjacent_link($link, $sq, false);
    }
    
    function get_search_context_adjacent_link($link, $sq, $previous) {
        global $post, $search_context_query;
        if ( !$post ) return $link;
    
        if (empty($search_context_query)) {
            $search_context_query = get_posts(array(
                'posts_per_page' => -1,
                'post_type' => 'post',
                'post_status' => 'publish',
                'fields' => 'ids',
                's' => $sq,
                )
            );
        }
    
        $key = array_search($post->ID, $search_context_query);
    
        if ($previous) $key--;
        else $key++;
    
        if (!isset($search_context_query[$key])) return '';
    
        $adjpost = get_post($search_context_query[$key]);
    
        $title = $previous ? 'Previous Post' : 'Next Post';
        $rel = $previous ? 'prev' : 'next';
        $permalink = add_query_arg( array( 'sq' => $sq ), get_permalink($adjpost) );
    
        $string = '<a href="'.$permalink.'" rel="'.$rel.'">';
        $output = $string . $title . '</a>';
    
        return $output;
    }
    

    For a custom post type, you’ll probably have to change the ‘post_link’ filter to a ‘post_type_link’ filter. You’ll also need to adjust the function to check for the custom post type. Like so:

    ...
    if (is_search()) {
       add_filter('post_type_link','search_context_add_search_context',10,2);
    }
    ...
    

    and

    function search_context_add_search_context($link, $post) {
        if ($post->post_type != 'YOUR-CUSTOM-TYPE') return $link;
        $sq = get_search_query();
        if ( !empty( $sq ) )
            $link = add_query_arg( array( 'sq' => $sq ), $link );
        return $link;
    }
    

    Down in the get_search_context_adjacent_link function, you’ll need to change the post_type value in the query there as well.

  2. That is not the way WordPress Works, meaning that once you enter s single post from the search results you lose the search context and WordPress can’t tell if you came for the search results, an archive, category page or whatever.

    The only way i can think of to by pass that is to create your custom search results page where you should add parameters to the single post link which will hold the search query string and the result’s number for example:

    $post_counter = 1;
    while (have_posts()){
        the_post();
        ?>
        <a href="<?php echo add_query_arg( array('sq' => get_search_query(),'offset_number' =>$post_counter), get_permalink() ); ?>"><?php the_title(); ?></a>
        <?php
        $post_counter = $post_counter + 1;
    }
    

    then in your single.php file where you use next_post_link and previous_post_link check if you came form search results before printing it and if you did then create a query to get the right links, something like:

    if (get_query_var('sq')){
        global $post;
        $temp = $post;
        //create a short query to get the right links based on search.
        $offset = (get_query_var('sq')) ? get_query_var('sq'): 0;
        $sq = get_posts(array(
            'posts_per_page' => 3, //we only need 3  PREVIOUS < this we know > NEXT
            'post_type' => 'post',
            'post_status' => 'published',
            's' => get_query_var('sq'), //this is to create a search query
            'offset', => $offset
        ));
        $counter = 0;
    
        foreach ($sq as $s){
            if ($offset == 0){ //this is the first result so no privous link should be printed only next
                if ($s->ID == $temp->ID){// look for current post as pivot
                    $counter = $counter +1;
                    if (isset ($sq[$counter])){
                        //next result exsits so we print it out
                        echo '<a href="'.add_query_arg( array('sq' => get_search_query(),'offset_number' =>($offset+1)), get_permalink($sq[$counter ]->ID) ).'">Next Post</a>';
                    }
                }
            }else{
                if ($s->ID == $temp->ID){ // look for current post as pivot
                    $counter = $counter - 1;
                    if (isset ($sq[$counter])){
                        //Previous result exsits so we print it out
                        echo '<a href="'.add_query_arg( array('sq' => get_search_query(),'offset_number' =>($offset-1)), get_permalink($sq[$counter ]->ID) ).'">Previous Post</a>';
                    }
                    $counter = $counter + 2;
                    if (isset ($sq[$counter])){
                        //next result exsits so we print it out
                        echo '<a href="'.add_query_arg( array('sq' => get_search_query(),'offset_number' =>($offset+1)), get_permalink($sq[$counter ]->ID) ).'">Next Post</a>';
                    }
                }
            }
            $counter = $counter +1;
        }   
    
    }else{
        //pring your regular links using next_post_link and previous_post_link
    }
    

    It’s messy and probably not the most efficient way to do it but it works.