If you look ate the source code where stickies are included, we find the following condition before WP_Query
carries on to include sticky posts
if ( $this->is_home
&& $page <= 1
&& is_array($sticky_posts)
&& !empty($sticky_posts)
&& !$q['ignore_sticky_posts']
) {
The big game player is the is_home
condition. Conditionals inside WP_Query
is set according to the arguments passed to it, and not the page where the query happens. Because you pass cat
as an argument, is_home
will be set to false, and is_category
will be set to true. Because is_home
is set to false, the above condition fails and stickies are not included.
POSSIBLE WORK-AROUND - Untested
What we can do, is to manually set is_home
to true via pre_get_posts
. All we need to do is to pass 'wpse_is_home' => true,
to your custom WP_Query
instance
Then we run our pre_get_posts
action as follow
add_action( 'pre_get_posts', function ( $q )
{
if ( true === $q->get( 'wpse_is_home' ) )
$q->is_home = true;
});
EDIT
We would probably only want to show stickies which has relevance to the queried category. In that case, we should remove stickies that does not belong to the specific queried category
We can try the following, still inside our pre_get_posts
action
add_action( 'pre_get_posts', function ( $q )
{
remove_action( current_action(), __FUNCTION__ );
if ( true !== $q->get( 'wpse_is_home' ) )
return;
// Make sure get_sub_field exists, if not, bail
if ( !function_exists( 'get_sub_field' ) )
return;
// Lets query everything to keep code clean
$meta_cat = get_sub_field( 'category' ); // Copied from your code
$meta_cat = filter_var( $meta_cat, FILTER_VALIDATE_INT );
// Make sure we have a value, if not, bail
if ( !$meta_cat )
return;
$q->set( 'cat', $meta_cat );
// Set pagination if recent_ppp exists
$ppp = get_sub_field( 'recent_ppp');
$ppp = filter_var( $ppp, FILTER_VALIDATE_INT );
if ( $ppp )
$q->set( 'posts_per_page', $ppp );
// Set is_home to true
$q->is_home = true;
// Get all stickies
$stickies = get_option( 'sticky_posts' );
// Make sure we have stickies, if not, bail
if ( !$stickies )
return;
// Query the stickies according to category
$args = [
'post__in' => $stickies,
'posts_per_page' => -1,
'ignore_sticky_posts' => 1, // Ignore stickies
'cat' => $meta_cat,
'orderby' => 'post__in',
'order' => 'ASC',
'fields' => 'ids' // Get only post ID's
];
$valid_sticky_ids = get_posts( $args );
// Make sure we have valid ids
if ( !$valid_sticky_ids ) {
$q->set( 'post__not_in', $stickies );
return;
}
// Remove these ids from the sticky posts array
$invalid_ids = array_diff( $stickies, $valid_sticky_ids );
// Check if we still have ids left in $invalid_ids
if ( !$invalid_ids )
return;
// Lets remove these invalid ids from our query
$q->set( 'post__not_in', $invalid_ids );
});
Before we run our custom query, we would need to make sure that get_sub_field
exists, and most importantly, that get_sub_field( 'category' )
is set and valid. This will avoid catastrophic failure which will lead to all posts being returned, regardless.
Your WP_Query
can look something like this as we only need to pass 'wpse_is_home' => true
:
if ( function_exists( 'get_sub_field' )
&& filter_var( get_sub_field( 'category' ), FILTER_VALIDATE_INT )
) {
$args = [
'wpse_is_home' => true
];
$posts_array = new WP_Query( $args ); // DO NOT USE $posts
// Run your loop as normal
}
LAST EDIT
Code is tested and working as expected
$$meta_cat