Take the 2-minute tour ×
WordPress Development Stack Exchange is a question and answer site for WordPress developers and administrators. It's 100% free, no registration required.

This is what I'm using to output my post navigation:

<?php if (is_single() ) { ?>
<nav class="post-navigation">
    <h1 class="visuallyhidden">Post Navigation</h1>
    <ul>
        <li class="prev"><?php previous_post_link('%link', '%title', 'in_same_cat'); ?></li>
        <li class="next"><?php next_post_link('%link', '%title', 'in_same_cat'); ?></li>
    </ul>
</nav>
<?php } ?>

This will restrict the navigation to the current category (sub and parent category).

I'd like to restrict this to only the current sub category. What would be the best way to do this?

share|improve this question
add comment

2 Answers

up vote 2 down vote accepted
+50

As @Ravs mentions the third parameter in next_post_link()/previous_post_link() should be a boolean value indicating whether to restrict the link to a post in the same category. By default this is false. If set to true, it will choose the next (or previous) post which is in the same category as the current post. This includes all parent & child categories.

The functions accept a third parameter: an array of category IDs to be excluded - but as pointed out in the comments, you cannot exclude categories to which the post belongs if you set the 'in same category' argument to true.

So one solution is to set 'in same category' to false, and exclude all terms which are not associated with a post, or which are, but have a child who is also associated with a post. (This is fairly messy, you can use the SQL filters if you prefer)

The following returns terms that are associated with a post, and who have child terms also associated with the post:

/**
 * Returns a term IDs of terms that are associated with a post, and who have
 * child terms also associated with the post.
 *
 * Please note, 'parent' is a slight misnomer. If you have category structure: 
 *    Cat A > Sub-Cat B > Sub-Sub-Cat C
 * and a post belongs to A and B, but not C. 'B' is not considered a parent term
 * for this post.
 *
 * @link http://wordpress.stackexchange.com/questions/101633/restrict-post-navigation-to-current-sub-menu/102616#102616
 * @param int $post_id. Optional, defaults to the 'current' post.
 * @return array An array of 'parent' term IDs
 */
function wpse101633_get_parent_terms( $post_id = 0 ) {

    $post_id = ( $post_id ? $post_id : get_the_ID() );
    $terms = get_the_terms( $post_id, 'category' );
    $terms_with_children = array();

    if( $terms ){
       foreach( $terms as $t ) {
            if( $t->parent && !in_array(  $t->parent, $terms_with_children ) ){
                $terms_with_children[] = $t->parent;
            }
        }
    }

    return $terms_with_children ;
}

Example usage:

Health warning: I don't perform any checks so you may get errors if the post has no terms etc. The following logic to get $exclude_these should be wrapped up in a function and moved out of the template for readability. I got lazy.

<?php if (is_single() ) { 

 $all_term_ids = get_terms( 'category', array( 'fields' => 'ids' ) );
 $post_terms = get_the_terms( get_the_ID(), 'category' );
 $post_terms = wp_list_pluck( $post_terms, 'term_id' );
 $parent_post_terms = wpse101633_get_parent_terms();
 $child_terms = array_diff( $post_terms, $parent_post_terms );

 //Exclude terms not associated with post, or that have a child who do.
 $exclude_these = array_diff( $all_term_ids, $child_terms );

 ?>
<nav class="post-navigation">
    <h1 class="visuallyhidden">Post Navigation</h1>
    <ul>
        <li class="prev"><?php previous_post_link( '%link', '%title', false, $exclude_these ); ?></li>
        <li class="next"><?php next_post_link( '%link', '%title', false, $exclude_these ); ?></li>
    </ul>
</nav>
<?php } ?>
share|improve this answer
    
fourth parameter in previos_post_link and next_post_link i.e. $excluded_categories is a string parameter while in your case you are passing $terms_with_children which is an array more info.and in function wpse101633_get_parent_terms, i think if condition should be like if(terms) otherwise function return empty array. –  Ravs Jun 11 '13 at 12:26
    
@Ravs Good catch on the if( $terms ) - I've fixed that now. R.e. the fourth parameter - my implementation is correct. The Codex is out of date. Always look at the source :). –  Stephen Harris Jun 11 '13 at 13:08
    
Codex has now been updated too. –  Stephen Harris Jun 11 '13 at 13:17
    
Many thanks for a detailed answer. This doesn't change the output for me I'm afraid though. Posts in other sub-categories are displaying as links - I'd like just posts in the current sub category. –  AlecRust Jun 13 '13 at 0:24
1  
i think this will not working because of this if condition in WordPress core source code, in this condition array_diff cause of $excluded_categories to be empty –  Ravs Jun 13 '13 at 2:57
show 4 more comments

you code may be look like

<?php if (is_single() ) { ?>
<nav class="post-navigation">
    <h1 class="visuallyhidden">Post Navigation</h1>
    <ul>
        <li class="prev"><?php previous_post_link('%link', '%title', TRUE); ?></li>
        <li class="next"><?php next_post_link('%link', '%title', TRUE); ?></li>
    </ul>
</nav>
<?php } ?>

put TRUE instead in_same_cat.

in_same_cat is a parameter which have value TRUE or FAlSE(default)

Important link:

previous_post_link

share|improve this answer
    
This doesn't change the output for me I'm afraid. Posts in other sub-categories are displaying as links - I'd like just posts in the current sub category. –  AlecRust Jun 13 '13 at 0:19
    
can you give me a example that helps me to understand more about your problem. –  Ravs Jun 13 '13 at 1:12
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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