1

I have an archive page that pulls two different custom post types into an archive using WP_Query(). Because there is a home page to the site which shows 9 of the custom post type posts, I offset the archive page by 9 posts.

The code for this is:

$homePageArticles = new WP_Query(array(
        'posts_per_page' => 18,
        'offset' => 9,
        'post_type'=> array('articles', 'featured')
    ));

    while(  $homePageArticles->have_posts()){
            $homePageArticles->the_post(); ?>

            // HTML

<?php } ?>

However on this archive page the <?php echo paginate_links();?> function to show the pagination pages doesn't work.

I can click through the numbers or use the next and previous arrows, but it just shows the same posts on each page.

The pagination code I'm using is:

<p>
    <?php echo paginate_links(array(
        'prev_text' => 'NEWER',
        'next_text' => 'OLDER',
        'mid_size' => 0
    ));?>
</p>

On normal custom archive pages on the site (which don't use WP_Query();) the pagination works fine.

Any help or assistance would be amazing.

0
1

Note that with a custom WP_Query class instance like the $homePageArticles in your case, you should pass the max_num_pages property of the class instance to paginate_links():

paginate_links( array(
    'total' => $homePageArticles->max_num_pages,
) )

However, that alone will not work with custom offset which breaks the pagination, hence for example you got this issue:

it just shows the same posts on each page

But it can be fixed and it's quite easy:

  1. Calculate the offset based on the current page number and pass the offset to WP_Query:

    // Current page number.
    $paged = max( 1, get_query_var( 'paged' ) );
    
    $per_page     = 18; // posts per page
    $offset_start = 9;  // initial offset
    $offset       = $paged ? ( $paged - 1 ) * $per_page + $offset_start : $offset_start;
    
    $homePageArticles = new WP_Query( array(
        'posts_per_page' => $per_page,
        'offset'         => $offset,
        'post_type'      => 'articles',
        // No need to set 'paged'.
    ) );
    
  2. Recalculate the max_num_pages property and pass it to paginate_links():

    $homePageArticles->found_posts   = max( 0, $homePageArticles->found_posts - $offset_start )
    $homePageArticles->max_num_pages = ceil( $homePageArticles->found_posts / $per_page );
    
    while ( $homePageArticles->have_posts() ) ...
    
    echo paginate_links( array(
        'current' => $paged,
        'total'   => $homePageArticles->max_num_pages,
        ...
    ) );
    

But then, if you're making the custom WP query in an archive template, e.g. archive-articles.php (archive-<post type>.php)

Then you should just forget that custom WP query.

And instead, use the pre_get_posts hook to filter the main WP query args (to set the custom offset), then use the found_posts hook to make sure we have the correct max_num_pages value, then just loop through the posts in the main query.

  1. In the theme functions file:

    function my_pre_get_posts( $query ) {
        if ( ! is_admin() && $query->is_main_query() &&
            is_post_type_archive( 'articles' )
        ) {
            $query->set( 'offset_start', 9 );
            $query->set( 'posts_per_page', 18 );
        }
    
        if ( $offset = $query->get( 'offset_start' ) ) {
            $per_page = absint( $query->get( 'posts_per_page' ) );
            $per_page = $per_page ? $per_page : max( 1, get_option( 'posts_per_page' ) );
    
            $paged = max( 1, get_query_var( 'paged' ) );
            $query->set( 'offset', ( $paged - 1 ) * $per_page + $offset );
        }
    }
    add_action( 'pre_get_posts', 'my_pre_get_posts' );
    
    function my_found_posts( $found_posts, $query ) {
        if ( $offset = $query->get( 'offset_start' ) ) {
            $found_posts = max( 0, $found_posts - $offset );
        }
    
        return $found_posts;
    }
    add_filter( 'found_posts', 'my_found_posts', 10, 2 );
    
  2. Then in your archive template:

    // No need for the "new WP_Query()".
    while ( have_posts() ) : the_post();
        ... your code.
    endwhile;
    
    // No need to set 'current' or 'total'.
    echo paginate_links( array(
        'prev_text' => 'NEWER',
        'next_text' => 'OLDER',
        ...
    ) );
    

And actually, with the custom functions in #1 above, you could simply use the custom offset_start arg with any WP_Query instances:

// Current page number.
$paged = max( 1, get_query_var( 'paged' ) );

$homePageArticles = new WP_Query( array(
    'posts_per_page' => 18,
    'offset_start'   => 9, // <- set this
    'post_type'      => 'articles',
    // No need to set 'paged' or 'offset'.
) );

while ( $homePageArticles->have_posts() ) ...

echo paginate_links( array(
    'current' => $paged,
    'total'   => $homePageArticles->max_num_pages,
    ...
) );
8
  • Hi @SallyCJ this doesn't work. I either get a white screen or no pagination. – The Chewy Mar 31 '20 at 16:44
  • Hi @TheChewy, I have revised the answer. That should work, but please check it out and let me know. – Sally CJ Apr 1 '20 at 15:39
  • 1
    Thanks @SallyCJ that does work now. It's really appreciated. One other good thing about this is it finally made me start learning about actions and filters, it's early days obviously, but a basic grasp of the concept of Hooks seems to be sinking in. – The Chewy Apr 2 '20 at 0:24
  • 1
    I'm glad to hear that and sorry, I should've asked what/which archive that you're referring to in the first place.. but I simply ended up assuming it's a static Page that lists your posts using a custom Page Template.. maybe because of the "custom archive pages" in the question.. :p Anyway, good luck! :) – Sally CJ Apr 2 '20 at 0:44
  • 1
    Wonderful. Thank you. That's been marked as correct again. – The Chewy Aug 20 '20 at 0:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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