4

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.

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.

1
  • Interesting problem. I don't think it will be possible without passing query parameters in the URL. If you are fine with that then I think it can be done. Maybe someone else would have a better suggestion. Commented Sep 15, 2011 at 10:20

2 Answers 2

7
+200

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.

5
  • Thanks Otto, that seems very neat. I can't get it to work though. I'm using the search on a custom post type, I changed the post_type value, without success.
    – mike23
    Commented Sep 16, 2011 at 6:12
  • Well, you should have said "custom post type" in the original question then, shouldn't you? Without knowing that bit of information, such as the name of your post type and how it's defined, etc, etc, nobody can solve your problem. These sorts of details matter, man. The code above works for "posts". If you're not using posts, then it's not going to work because the links will be wrong.
    – Otto
    Commented Sep 16, 2011 at 12:58
  • Sorry Otto, as "post" is just another post_type, I assumed it would be portable. Until now I had not encountered any WP code for posts that was not applicable to custom post types. Do you think there's a way to adapt the code? :)
    – mike23
    Commented Sep 16, 2011 at 14:55
  • Posts aren't pages, and they aren't custom post types either. So yeah, it's not portable without adjustments. I'll edit the answer to include the changes needed.
    – Otto
    Commented Sep 16, 2011 at 15:00
  • Sorry I should have clarified that at the beginning. Thanks for the update.
    – mike23
    Commented Sep 19, 2011 at 7:02
1

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.

2
  • 1
    Few minor problems which I corrected in my other answer: - get_query_var only works with declared query vars - your counter doesn't work because it doesn't account for pagination.. nice idea, but it doesn't work in real usage - it's just easier to do fields=>ids to get only the ids, which is a hella fast query and then you can array_search it to get prev/next post id But, the basic idea of passing the search query along is sound and frankly the only way to do it. Props.
    – Otto
    Commented Sep 16, 2011 at 2:53
  • Yea i let pagination since i posted this as an example ,which could be also added as a query argument. As for the declaration of the query cars i simply forgot to paste the function that does that in my answer. And i agree in 100% that using fields => ids would be much faster. Thanks.
    – Bainternet
    Commented Sep 16, 2011 at 9:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.