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.

What I am looking to do:

Setup WP_Query pagination in a single-custom-post-type.php template file

What I have done

1) Created a post type called "authors". Each post within that post type is an individual author.

2) Standard edit post screen pages contain a dropdown which lists all posts (authors) from the authors custom post type.

3) Created a single-authors.php template which query's all posts with the author metadata from the dropdown, so the result is a list of posts that have the same author assigned to them (kind of like an archive):

<?php

// set the "paged" parameter (use 'page' if the query is on a static front page)
global $paged;

/*We need this here to add and maintain Pagination if Template is assigned to Front Page*/
if ( get_query_var( 'paged' ) ) {
    $paged = get_query_var('paged');
} elseif ( get_query_var( 'page' ) ) {
    $paged = get_query_var( 'page' );
} else {
    $paged = 1;
}

$args = array(
    'posts_per_page'    =>  10,
    'meta_key'          => 'author_select',
    'meta_value'        => $author_id,
    'paged'             => $paged,
);

$temp = $wp_query;
$wp_query = NULL;

$wp_query = new WP_Query($args);

?>

<?php if( $wp_query->have_posts() ) : ?>    
    <?php while ( $wp_query->have_posts() ) : $wp_query->the_post(); ?>
    <?php // Successfully outputs the results of the above query, so I've omitted the code from this example. ?>

    <?php endwhile; ?>

<div class="single_navigation"> 
    <?php if( get_adjacent_post( false, '', true ) ) { ?>
        <span class="prev"><?php previous_post_link( '&lt; %link' ) ?></span>
    <?php } ?>

    <?php if( get_adjacent_post( false, '', false ) ) { ?>
        <span class="next"><?php next_post_link( '%link &gt;' ) ?></span>
    <?php } ?>
    </div><!--/single navigation-->
<?php endif; ?>

<?php
    $wp_query = null;
    $wp_query = $temp;
    wp_reset_query();
?>

What I am stuck on

The pagination links do not appear. I have done some research and have found that they use the $wp_query variable, however when I changed my query variable to $wp_query, the links appeared, but when clicked did nothing.

Any idea where I am going wrong?

Edit:

To answer some of your questions, my query successfully outputs the posts that I am querying, $author_id already has a value that I did not include in this code snippet. Similarly, I left out the actual content output within the while loop because that part is not the issue. The issue is that I need to paginate the content that already exists on this single post.

The purpose of this functionality was to allow posts to have custom authors (outside of the built in Users system) and this template's purpose is to output all of the posts for a given author (which is already working).

share|improve this question
3  
An upvote for a well formatted question, though you didnt describe why you're doing this, is it a related posts by the current author box? –  Tom J Nowell May 8 at 16:44
1  
I really do agree with @TomJNowell. +1 –  Pieter Goosen May 8 at 16:45
2  
Unrelated, but it is not necessary to echo the_title() –  Pieter Goosen May 8 at 16:47
2  
Is 'meta_value' => $author_id being filled from the dropdown mentioned? And when selecting each author via the dropdown, are the first ten posts showing correctly? –  Stephen S. May 8 at 16:52
1  
Have you made sure that $author_query->have_posts() returns true and that the $author_query actually contains any posts? –  kaiser May 8 at 17:06

2 Answers 2

up vote 7 down vote accepted

You have 2 problems.

First problem

The line

$paged = ( get_query_var( 'paged' ) ) ? get_query_var( 'paged' ) : 1;

will fail, because on singular post view, when the url contains '/page/XX/' the variable WordPress sets is 'page' and not 'paged'.

You may think to use 'page' instead of 'paged', but that will not work too, because once the 'page' variable is intended to be used for multi-page singular post (page separiation using <!--nextpage-->) and once the post is not multi-page WordPress will redirect the request to the url without '/page/XX/'.

This is what happen when you name your query variable $wp_query.

The solution, is to prevent that redirection, removing the function responsible for it, that is 'redirect_canonical' hooked into 'template_redirect':

So, in your functions.php add:

add_action('template_redirect', function() {
  if ( is_singular('authors') ) {
    global $wp_query;
    $page = (int) $wp_query->get('page');
    if ( $page > 1 ) {
      // convert 'page' to 'paged'
      $query->set( 'page', 1 );
      $query->set( 'paged', $page );
    }
    // prevent redirect
    remove_action( 'template_redirect', 'redirect_canonical' );
  }
}, 0 ); // on priority 0 to be able removing 'redirect_canonical' added with priority 10

Now WordPress will not redirect anymore and will set correclty the 'paged' query var.

Second problem

next_posts_link() and previous_posts_link() both check if ( ! is_single() ) to display pagination.

Now, is_single() is true in your case, because you are in a single post of 'author' type, so that functions can't work as you expect.

You have 3 possibilities:

  1. use query_posts to override the main query
  2. use a custom page template instead of a custom post type. Because is_single() is false for pages, and your code will work there.
  3. Write your own pagination function and use that

That's the code for solution number #3:

function my_pagination_link( $label = NULL, $dir = 'next', WP_Query $query = NULL ) {
  if ( is_null($query) ) $query = $GLOBALS['wp_query'];
  $max_page = (int) $query->max_num_pages;
  // only one page for the query, do nothing
  if ( $max_page <= 1 ) return;
  $paged = (int) $query->get('paged');
  if ( empty( $paged ) ) $paged = 1;
  $target_page = $dir === 'next' ?  $paged + 1 : $paged - 1;
  // if we are in 1st page and required previous or in last page and required next,
  // do nothing
  if ( $target_page < 1 || $target_page > $max_page ) return; 
  if ( null === $label )  $label = __( 'Next Page &raquo;' );
  $label = preg_replace('/&([^#])(?![a-z]{1,8};)/i', '&#038;$1', $label);
  printf( '<a href="%s">%s</a>', get_pagenum_link( $target_page ), $label );
}

and use it like so in the single-authors.php:

my_pagination_link( 'Older Entries', 'next', $author_query );
my_pagination_link( 'Newer Entries', 'prev', $author_query );
share|improve this answer
    
Very detailed answer, thank you! This is working for the English side of the site, but on the French side the pagination links don't do anything, they only refresh the page. I'm using WPML. –  Shane May 8 at 20:09
1  
@Shane so seems that WPML run some filter that prevent code to work. But is a so big plugin, and so I can't dig thousands lines of code to understand where is the confict. Try ask WPML support. –  G. M. May 8 at 22:30
    
@Shane The WPML Support forums - and if above works for (which I assume), then please mark it as solution. The WPML conflict is off topic/out of scope of this site anyway. –  kaiser May 9 at 1:03

Based on another similar WPSE question "Pagination is not working wp_query custom fields values", I would suggest adding global $paged; to the beginning of your template file:

global $paged;
$paged = ( get_query_var( 'paged' ) ) ? get_query_var( 'paged' ) : 1;
share|improve this answer
    
Thanks for the response! Unfortunately it doesn't appear to make any difference. The pagination links do not appear –  Shane May 8 at 18:32
    
Stephen, why should that help? Yes, this would bring it in from the global context, but so far you aren't doing anything with it. If you want to override the global, then use $GLOBALS['paged'] = get_query_var( 'paged' );. Else just use the globals content. –  kaiser May 8 at 18:34
    
@Shane Hmm, is the template page being used as a static front page? –  Stephen S. May 8 at 18:36
    
No, it is only being used as a single post template for the custom post type. I did try changing my query from $author_query to $wp_query and that seemed to make the pagination appear, but when I click on it the page refreshes and doesn't actually go to the next page, even though there are multiple pages. I've updated my answer to reflect those changes. –  Shane May 8 at 18:38
1  
The php.net explanation isn't that bad. I'd suggest just playing around with global and $GLOBALS. A bit of var_dump(), some functions and you're fine. To really understand it with a bit of try/error you will approx. need half an hour. This will help you much better understand functions vs. classes and the OOP approach as a whole as well. And it will clearly bring you closer to understanding namespaces :) –  kaiser May 8 at 18:45

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.