Wordpress - How to list some posts first in the loop based on post id
If you need to:
page the query
retain 12 posts per page instead of "sticking" the desired posts on top of the required 12
only need to show those posts on the first page
you can try the following
$ids_args = [
'post_type' => 'products'
'posts_per_page' => -1,
'orderby' => 'meta_value_num',
'meta_key' => '_price',
'order' => 'ASC',
'fields' => 'ids'
];
$all_posts_ids = get_posts( $ids_args );
// Make sure we have posts before continuing
if ( $all_posts_ids ) {
// Set all our posts that should move to the front
$move_to_front = [12,13,14,34];
// Add the array of posts to the front of our $all_posts_ids array
$post_ids_merged = array_merge( $move_to_front, $all_posts_ids );
// Make sure that we remove the ID's from their original positions
$reordered_ids = array_unique( $post_ids_merged );
// Now we can run our normal query to display 12 posts per page
$args = [
'post_type' => 'products'
'posts_per_page' => 12,
'post__in' => $reordered_ids,
'orderby' => 'post__in',
'order' => 'ASC',
'paged' => get_query_var( 'paged', 1 ),
];
$loop = new WP_Query( $args );
while( $loop->have_posts() ) {
$loop->the_post();
the_content();
}
wp_reset_postdata();
}
If you need these posts
to stick on top of the 12 posts on every page
in a paged query
you can run two queries as follow
// Set an array of id's to display in front
$move_to_front = [12,13,14,34];
// Run the query to display the posts you need in front
$args_front = [
'post_type' => 'products'
'posts_per_page' => count( $move_to_front ),
'post__in' => $move_to_front,
'orderby' => 'meta_value_num',
'meta_key' => '_price',
'order' => 'ASC',
];
$loop_front = new WP_Query( $args_front );
if( $loop_front->have_posts() ) {
while( $loop_front->have_posts() ) {
$loop_front->the_post();
the_content();
}
wp_reset_postdata();
}
// Now we can run our major loop to display the other posts
$args = [
'post_type' => 'products'
'posts_per_page' => 12,
'post__not_in' => $move_to_front,
'orderby' => 'meta_value_num',
'meta_key' => '_price',
'order' => 'ASC',
'paged' => get_query_var( 'paged', 1 ),
];
$loop = new WP_Query( $args );
if( $loop->have_posts() ) {
while( $loop->have_posts() ) {
$loop->the_post();
the_content();
}
wp_reset_postdata();
}
If you only need one page with those posts in front, then the solution by @birgire will do the job just fine
We could do that with two queries:
First we fetch the post ids we need with the first query and then we merge it with our sticky post ids and feed it into the second query, using the post__in
parameter for filtering and ordering:
$args = [
'post_type' => 'products'
'posts_per_page' => 12,
'orderby' => 'meta_value_num',
'meta_key' => '_price',
'order' => 'asc',
'ignore_sticky_posts' => true,
'fields' => 'ids',
];
$ids = get_posts( $args );
if( ! empty( $ids ) )
{
$stickies = [12,13,14,34];
$post__in = array_unique( array_merge( $stickies, $ids ) );
$args = [
'post__in' => $post__in,
'orderby' => 'post__in',
'ignore_sticky_posts' => true,
];
$loop = new WP_Query( $args );
// ... etc
}
where we adjust the $stickies
for the custom sticky posts.