Changing Posts Per Page and offset with pre_get_posts

Here is an answer I recently did on another question, the same as your. Because the answer was never upvoted or accepted, I could not mark this question as duplicate, so I have deleted the answer in the other post and reposting it here.

Please note, some points are not aimed at this issue and can be ignored, also, you will just need to make a few alterations on the values as I have not changed anything in the original post. So don’t be alarmed by some of the info in the answer 😉

REPURPOSED ANSWER

Here is a slight variation of an answer I have done on about the same scenario. What differs here is that you want less posts_per_page on page one

STEP 1

Remove query_posts. You should never ever use query_posts

Note: This function isn’t meant to be used by plugins or themes. As explained later, there are better, more performant options to alter the main query. query_posts() is overly simplistic and problematic way to modify main query of a page by replacing it with new instance of the query. It is inefficient (re-runs SQL queries) and will outright fail in some circumstances (especially often when dealing with posts pagination).

Replace it with the default loop

<?php if ( have_posts() ) : while ( have_posts() ) : the_post(); ?>

    <?php if ( 'regularproducts' == get_post_type()  ) : ?>

        //CONTENT HERE

    <?php endif; ?>

    <?php if ( 'wpsc-product' == get_post_type()  ) : ?>

        //CONTENT HERE

    <?php endif; ?>

<?php endwhile; endif; ?>

STEP 2

Use pre_get_posts to alter the main query to add your custom post_types to the main query to display on the home page.

STEP 3

Now, get the posts_per_page option set from the back end (which should be set to 300) and also set your offset which we are going to use. That will be 200 as you will need 100 posts on page one and 300 on the rest.

If you don’t want to alter the posts_per_page option, you can then just simply set the variable $ppg to 300

$ppg = get_option( 'posts_per_page' );
//$ppg = 300;
$offset = 200;

STEP 4

On page one, you’ll need to subtract the offset to posts_per_page

$query->set( 'posts_per_page', $ppp - $offset );

STEP 5

You must apply your offset to all subsequent pages, otherwise you will get a repetition of the last post of the page on the next page

$offset = ( ( $query->query_vars['paged']-1 ) * $ppp ) - $offset;
$query->set( 'posts_per_page', $ppp );
$query->set( 'offset', $offset ); 

STEP 6

Lastly, you need to add your offset to found_posts otherwise your pagination will will not show the last page

NOTE: This piece of code broke pagination on the search page. This is now fixed, see the updated code

function homepage_offset_pagination( $found_posts, $query ) {
    $offset = 200;

    if( $query->is_home() && $query->is_main_query() ) {
        $found_posts = $found_posts + $offset;
    }
    return $found_posts;
}
add_filter( 'found_posts', 'homepage_offset_pagination', 10, 2 );

ALL TOGETHER

This is how your complete query will look like that should go into functions.php

function tax_and_offset_homepage( $query ) {
  if ( !is_admin() && $query->is_home() && $query->is_main_query() ) {
    $query->set( 'post_type', array( 'regularproducts', 'wpsc-product' ) );

    $ppp = get_option( 'posts_per_page' );
//$ppp = 300;
    $offset = 200;
    if ( !$query->is_paged() ) {
      $query->set( 'posts_per_page', $ppp - $offset );
    } else {
      $offset = ( ( $query->query_vars['paged']-1 ) * $ppp ) - $offset;
      $query->set( 'posts_per_page', $ppp );
      $query->set( 'offset', $offset );
    }
  }
}
add_action('pre_get_posts','tax_and_offset_homepage');

function homepage_offset_pagination( $found_posts, $query ) {
    $offset = 200;

    if( $query->is_home() && $query->is_main_query() ) {
        $found_posts = $found_posts + $offset;
    }
    return $found_posts;
}
add_filter( 'found_posts', 'homepage_offset_pagination', 10, 2 );

Leave a Comment