I am writing custom ajax pagination for a Blog archive page and I am running into an issue with sticky posts.
The default functionality of sticky posts when using a WP_Query call is to sort all sticky posts at the top, unless explicitly stated otherwise.
My AJAX is functioning correctly with no errors and displays the requested posts correctly without issue and can be sorted by category and feature pagination (using posts_per_page and offset).
The only problem is that the posts retrieved are not listing sticky posts in the front but rather in their natural order. The correct group of posts are queried but they do not order correctly.
My argument is:
$args = array(
'posts_per_page' => 10,
'order' => 'DESC',
'cat' => (php value retrieved from AJAX, so it changes based on user input),
'offset' => (determined via data attributes to calculate page number. $offset = ( current_page_number - 1 ) * posts_per_page )
)
$blog_posts = new WP_Query( $args );
If I print_r $blog_posts, the order of the posts is in its default order (much like if you were to use get_posts instead of WP_Query )
If I use this exact query on any page (or post, custom post type post, archive, etc), the WP_Query function returns in the correct order with sticky posts on top. Is this a bug anyone else has run into? The code runs fine, I am wondering if this is a WordPress core bug?
I cannot do 2 separate queries -- one with only sticky posts, and then the rest excluding sticky posts because of custom pagination.
Thanks
Edit, with a solution of sorts:
After doing a lot of googling to no success, the following post gave me an idea:
WP_Query: Why is sticky post not first item in loop?
In it, it mentions that WP_Query checks the following before sorting Sticky Posts at the top:
if ( $this->is_home
&& $page <= 1
&& is_array($sticky_posts)
&& !empty($sticky_posts)
&& !$q['ignore_sticky_posts']
) {
Because I was running my query in a function independently called by AJAX, there was no Post or Page ID associated with this particular snippet of code. Therefore, WP_Query could not fulfill its conditional in order to do its default sticky_post function.
"get_posts" does not allow for sticky posts to show on top as it automatically sets "ignore_sticky_posts" to true.
Check out the WP Developers Page for get_posts (https://developer.wordpress.org/reference/functions/get_posts/). On line 1591 of the code snippet, you will see:
$r['ignore_sticky_posts'] = true;
being explicitly set right before the query is returned.
So, we can neither use WP_Query nor get_posts in this particular instance.
My solution was to do the following:
// get_posts of all the possible posts, with your desired parameters and post-per-page set to -1
$all_posts = get_posts( $args );
// check if there are any sticky posts
$sticky = get_option( 'sticky_posts' );
// there are sticky posts, do this psuedo-code
if ( is_array($sticky) && !empty($sticky) ) {
// do a get_post query for ONLY the sticky posts posts that are in your desired $args array
$args['post__in'] = $sticky;
$sticky_posts = get_posts( $args );
// merge $total_stories_id into the end of $sticky
$merged = array_merge( $sticky_posts, $all_posts );
// remove duplicates and re-indexes
$array = array_values( array_unique( $merged ) );
// return a slice of X elements, starting at the $offset location
$return_array = array_slice( $array , [calculated post offset], [your desired post-per-page] );
} else {
// else, do wordpress default (wp_query or get_posts, it doesn't matter)
$return_array = get_posts( $args );
}
For the sake of cutting down the array sizes being passed about, I did:
wp_list_pluck( [array], 'ID' );
on the 2 get_posts arrays. You don't have to do this, it really depends on what kind of data you are passing back via AJAX.
I hope this helps anyone else!