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.