2

In author.php I have two loops: the default main loop that gets all posts from that author, and another custom WP_Query called $secondloop. Both queries have posts_per_page set to 3. Now I'd like to paginate both query results.

For this I've added an argument to $secondloop = new WP_Query($args):

"paged" => (get_query_var('paged')) ? get_query_var('paged') : 1;

(read: set this query's page to the same page as the main query's).

THE PROBLEM:

  • If the main loop gives me 5 posts and my $secondloop gives me 2, then calling page 2 works no problemo. It shows me the remaining 2 posts from the main loop and none from my $secondloop (since it doesn't go inside while ($secondloop->have_posts())).

  • BUT if the main loop has 2 posts and $secondloop has 5, then calling page 2 results in 404 :( I obviously want $secondloop's remaining 2 posts instead.

Changing anything inside the author.php template doesn't help, because it seems that no PHP code in there is executed any more, once WordPress detects that max_num_pages from the main loop is exceeded.

How can I "override" that behaviour?


THE CODE:

author.php (just the useful bit)

<?php if (have_posts()) { ?>
    <div class="row archive-grid">
        <?php while (have_posts()) : the_post();
            include(locate_template('parts/loop-archive-grid.php'));
            endwhile; ?>
    </div>
<?php }

$paged = (get_query_var('paged')) ? get_query_var('paged') : 1;
$secondloop = new WP_Query(array(
    "post_type"      => "musikalbum",
    "posts_per_page" => 3,
    "author_name"    => get_query_var('author_name'),
    "paged"          => $paged
));

if ($secondloop->have_posts()) { ?>
    <div class="row archive-grid">
        <?php while ($secondloop->have_posts()) : $secondloop->the_post();
            get_template_part( 'parts/loop', 'album-grid' );
        endwhile; ?>
    </div>
    <?php author_page_navi( $loop );
} else {
    joints_page_navi();
} ?>

Setting the posts_per_page for my main query in my functions.php

function my_post_queries( $query ) {
  if (!is_admin() && $query->is_main_query()){
    if (is_author()){
      $query->set('posts_per_page', 3);
    } else {
      $query->set('posts_per_page', 9);
    }
  }
}
add_action( 'pre_get_posts', 'my_post_queries' );

EDIT: ATTEMPT 1

Milo pointed me to an answer that suggested to add this to my functions.php:

function my_404_override() {
  global $wp_query;

  if (is_author()) {
    status_header( 200 );
    $wp_query->is_404 = false;
  }
}
add_filter('template_redirect', 'my_404_override');

But it still shows me a 404 error. If I remove the if clause, then it leads me to the index.php which is not reeeally what I intended. So I need that if clause...but WordPress doesn't seem to know it yet!

Pasting the code directly to the template didn't work either, even if it's just these three lines before get_header();:

global $wp_query;
status_header( 200 );
$wp_query->is_404 = false;

EDIT: ATTEMPT 2

I ditched template_redirect for hooking into pre_get_posts:

function my_post_queries( $query ) {
  if (!is_admin() && $query->is_main_query()){
    if (is_author()){
      $query->set('posts_per_page', 3);
      // NEW CODE:
      $paged = (get_query_var('paged')) ? intval(get_query_var('paged')) : 1;
      global $original_page_request; // saves the original value of $paged because I'm about to alter it!
      if ($paged > $query->max_num_pages) {
        $query->set('paged', $query->max_num_pages);
        $original_page_request = $paged;
    } else {
      $query->set('posts_per_page', 9);
    }
 }
}
add_action( 'pre_get_posts', 'my_post_queries' );

The idea was to manually reset paged for the main query when it exceeds max_num_pages and store the "actual page" in global $original_page_request for my $secondloop to set its paged parameter to. Unfortunately though, it doesn't work. I was so happy not to see a 404 any more, but now it automatically sets the main query page to 0. Why 0?!

Because in pre_get_posts, code is executed before WordPress got its posts. There's access to $query but that's just for setting query variables, there are no posts retreived, and thus no max_num_pages to calculate from.

So that means I have no idea when I'm about to exceed max_num_pages.

I then copied the new code from my_post_queries( $query ) to another function which hooks into wp. There, max_num_pages is a valid number and is_author() is correct too. As soon as I exceed the number of posts, still no luck. 404. I could set $wp_query->is_404 = false but that returns me to the index yet again.

3
  • Can you share your code please?
    – Michelle
    Commented Nov 15, 2016 at 3:47
  • 1
    It's possible, but a bit tricky. Maybe AJAX pagination is an option for you? Anyway, this answer might point you in a useful direction.
    – Milo
    Commented Nov 16, 2016 at 4:27
  • I have attempted to fix it using your answer but it did not work still. Details are in my edit. Commented Nov 16, 2016 at 20:07

1 Answer 1

3

On every page load, WordPress executes these functions in their respective order:

init();
parse_request($query_args);
send_headers();
query_posts();
handle_404();
register_globals();

where the main query is finished after query_posts(). Note that in this point in time, WordPress knows the template AND all the query variables like max_num_pages.

In handle_404(), if the main query has any posts, the function returns and WordPress continues to load the template. Else it sends a 404 HTTP status and loads the 404 template instead.

Since WordPress 4.5, there is a filter hook called pre_handle_404. If it's set to true, handle_404() returns (whether or not the main query has any posts!) and the template continues to load.

So after a lot of fiddling around, this finally works. Add this to your functions.php:

function ignore_404_past_max_page($bool = false, $query) {
  $paged = (get_query_var('paged')) ? intval(get_query_var('paged')) : 1;
  if (is_author() && ($paged > $query->max_num_pages))
    $bool = true;
  return $bool;
}
add_filter( 'pre_handle_404', 'ignore_404_past_max_page', 10, 2);

That's it! No editing of existing functions, clean and does exactly what you'd want it to do: prevent WP from 404ing. Just make sure to update WordPress to version 4.5 (and above)!

An unwanted side effect may be that now you can paginate untill PHP_INT_MAX and get empty pages (unless you add some get_the_author_meta() and get_wp_user_avatar() for example). You could probably memorize the maximum page number for each author in the database, then query it and compare it to $paged to set $bool to false in the function above. But I'm really happy with how it works now.

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.