Because this is suppose to be the main loop on your home page, there is a far better way to achieve this without running any custom query at all. You should never replace the main query with a custom one. I'm not going to go into details as I have already done a quite extensive answer on this which you must go and read.
We can use the main query and modify it to our free will to adjust it to what we need throught the filters and actions available
THE MASTER PLAN - The lego parts
You need 22 posts on page one, and from there on just 11. The tricky parts are
Page 1 where we need the first 11 posts in random order, then the next 11 in normal order.
Recalculating pagination as we will have the last page 404'ing in most cases depending on post count
What we need to do is:
Most important, remove the custom queries and go back to the main loop on index.php
. Your index.php
should look like the following: (Adjust to your exact needs, this is the bare minimum)
if ( have_posts() ) {
while ( have_posts() ) {
the_post();
// Your markup and template tags to display posts
}
// Your paging function
} else {
// Your message to display when there is no posts
}
Set posts per page to 11. We can do that manually or use the setting in the backend
Through pre_get_posts
we will add an extra 11 posts to page 11. At this stage you should have 22 posts on page one, sorted by date. The other pages should have 11 posts with most probably the last page 404'ing.
To sort out pagination, we will need to filter the amount of found posts through the found_posts
filter. The $max_num_pages
property in the query object, which is responsible for calulating pagination, uses the amount of posts per page and the amount of found posts to calculate how many pages there will be. After this is sorted, everything will work, except the first 11 posts on page one which will not be random
To randomize the first 11 posts on page one, we will rearrange the post order of the posts through the the_posts
filter. This filter executes before the loop is actually run to display the posts. After this is done, you should have the result you are after
THE CODE - Putting our lego blocks to use
Few notes
All code is untested and but might still be buggy. Please let me know if you have any issues
The code requires at least PHP 5.4
Adjust as needed. All code will go into a custom plugin (recommended) or in your theme's function file
PART ONE - The helping hands
Because we will have repetitive code, lets create filterable helpers
/**
* Return the amount of posts per page, which by default is 11
*/
function wpse_219896_ppp ()
{
return apply_filters( 'wpse_219896_ppp', 11 );
}
/**
* Returns the offset to use, which by default is 11
*/
function wpse_219896_offset ()
{
return apply_filters( 'wpse_219896_offset', 11 );
}
We can simply adjust any of these function's output with the following example:
add_filter( 'wpse_219895_ppp', function ( $ppp )
{
return 10;
}
This is just done to keep the code maintainable
PART TWO - pre_get_posts
We will now set our posts per page and offsets throught the pre_get_posts
action
add_action( 'pre_get_posts', function ( $q )
{
if ( $q->is_home() // Only target the home page
&& $q->is_main_query() // Only target the main query
) {
// First set our variables
$ppp = wpse_219896_ppp();
$offset = wpse_21896_offset();
$current_page = get_query_var( 'paged', 1 );
// Target the first page first
if ( !$q->is_paged() ) {
// Add our offset to $ppp
$q->set( 'posts_per_page', ( $ppp + $offset ) );
} else {
/**
* Lets target all the paged pages now.
*
* Because we are breaking the internal calculation for post offset and pagination,
* we must manually recalculate and set it
*/
$paged_offset = $offset + ( ( $current_page -1 ) * $ppp );
$q->set( 'posts_per_page', $ppp );
$q->set( 'offset', $paged_offset );
}
}
});
We have now taken care of the posts per page issue, page one will have 22 posts, the rest will have 11
PART THREE - found_posts
filter
We now need to adjust for pagination to work correctly
add_filter( 'found_posts', function ( $found_posts, $q )
{
if ( $q->is_home() // Only target the home page
&& $q->is_main_query() // Only target the main query
) {
$offset = wpse_219896_offset();
// Make sure we have atleat more posts found than the offset
if ( $offset >= $found_posts )
return $found_posts;
$found_posts = $found_posts - $offset;
}
return $found_posts;
}, 10, 2 );
PART FOUR - the_posts
filter
All we now need to do is to sort the first 11 posts on page one randomly
add_filter( 'the_posts', function ( $posts, $q )
{
if ( $q->is_home() // Only target the home page
&& $q->is_main_query() // Only target the main query
&& !$q->is_paged() // Only target page one
) {
$offset = wpse_219896_offset();
// Make sure that have more than 11 posts, if not, randomly sort all
if ( $offset >= $q->post_count ) {
shuffle( $posts );
return $posts;
}
/**
* We have more posts than our offset, lets split the array and
* randomize the first set of posts
*/
$array_1 = array_slice( $posts, 0, $offset );
$array_2 = array_slice( $posts, $offset, '', true );
// Randomize array_1
shuffle( $array_1 );
// Merge the two slices into one array again
$posts = array_merge( $array_1, $array_2 );
}
return $posts;
}, 10, 2 );
This should take care of everything.
ALL TOGETHER NOW
We can simplify this into a plugin. Here is how the final code could look like
<?php
/*
Plugin Name: Random posts per page
Plugin URI: https://wordpress.stackexchange.com/q/219896/31545
Description: Different amount of posts per page with x amount shown randomly
Version: 1.0.0
Author: Pieter Goosen
Author URI: https://wordpress.stackexchange.com/users/31545/pieter-goosen
License: GPL2
License URI: https://www.gnu.org/licenses/gpl-2.0.html
*/
/**
* Return the amount of posts per page, which by default is 11
*/
function wpse_219896_ppp ()
{
// Filter the amount of posts per page
return apply_filters( 'wpse_219896_ppp', 11 );
}
/**
* Returns the offset to use, which by default is 11
*/
function wpse_219896_offset ()
{
// Filter the offset of posts
return apply_filters( 'wpse_219896_offset', 11 );
}
add_action( 'pre_get_posts', function ( $q )
{
if ( $q->is_home() // Only target the home page
&& $q->is_main_query() // Only target the main query
) {
// First set our variables
$ppp = wpse_219896_ppp();
$offset = wpse_219896_offset();
$current_page = get_query_var( 'paged', 1 );
// Target the first page first
if ( !$q->is_paged() ) {
// Add our offset to $ppp
$q->set( 'posts_per_page', ( $ppp + $offset ) );
// Filter the_posts to randomize first x amount of posts
add_filter( 'the_posts', function ( $posts, $q ) use ( $ppp, $offset )
{
remove_filter( current_filter(), __FUNCTION__ );
if ( !$q->is_main_query() )
return $posts;
// Make sure that have more than $offset posts, if not, randomly sort all
if ( $offset >= count( $q->posts ) ) {
shuffle( $posts );
return $posts;
}
/**
* We have more posts than our offset, lets split the array and
* randomize the first set of posts
*/
$array_1 = array_slice( $posts, 0, $offset );
$array_2 = array_slice( $posts, $offset, $ppp, true );
// Randomize array_1
shuffle( $array_1 );
// Merge the two slices into one array again
$posts = array_merge( $array_1, $array_2 );
return $posts;
}, 10, 2 );
} else {
/**
* Lets target all the paged pages now.
*
* Because we are breaking the internal calculation for post offset and pagination,
* we must manually recalculate and set it
*/
$paged_offset = $offset + ( ( $current_page -1 ) * $ppp );
$q->set( 'posts_per_page', $ppp );
$q->set( 'offset', $paged_offset );
}
// Filter the found_posts property of the query
add_filter( 'found_posts', function ( $found_posts, $q ) use ( $offset )
{
remove_filter( current_filter(), __FUNCTION__ );
if ( !$q->is_main_query() )
return $found_posts;
// Make sure we have at leat more posts found than the offset
if ( $offset >= $found_posts )
return $found_posts;
return $found_posts - $offset;
}, 10, 2 );
}
});
wp_reset_query
?