How to return results of a get_posts() in explicitly defined order

The question:

I’m trying to create a loop of explicity ordered posts, for example:

<?php $args = array(
    'include'         => '1,3,8,4,12' ); ?>

<?php get_posts( $args ); ?> 

The results are ordered by date by default, and there is no orderby option to return the posts in the order they were entered. There have been multiple bug/feature requests posted about this in Trac, but so far no luck. I’ve mucked around in the core files a bit but haven’t gotten anywhere with it.

Can anyone suggest a workaround for this behavior?

Cheers,
Dalton

The Solutions:

Below are the methods you can try. The first solution is probably the best. Try others if the first one doesn’t work. Senior developers aren’t just copying/pasting – they read the methods carefully & apply them wisely to each case.

Method 1

Okay, I was determined to find a way to do this, and I think I’ve got it. I had hoped to find a simpler solution and avoid having to use a new WP_Query object, but it’s just too ingrained into how the loop works. First, we have a couple of utility functions:

// Set post menu order based on our list  
function set_include_order(&$query, $list) {
    // Map post ID to its order in the list:
    $map = array_flip($list);

    // Set menu_order according to the list     
    foreach ($query->posts as &$post) {
      if (isset($map[$post->ID])) {
        $post->menu_order = $map[$post->ID];
      }
    }  
}

// Sort posts by $post->menu_order.                                 
function menu_order_sort($a, $b) {
  if ($a->menu_order == $b->menu_order) {
    return 0;
  }
  return ($a->menu_order < $b->menu_order) ? -1 : 1;
}

These will allow us to set the menu_order property based on our own list, and then sort the posts in a query object based on that.

Here’s how we query and sort the posts:

$plist = array(21, 43, 8, 44, 12);
$args = array(
  'post_type' => 'attachment',
  'post_status' => 'any',
  'post__in' => $plist 
);

// Create a new query  
$myquery = new WP_Query($args);

// set the menu_order
set_include_order($myquery, $plist);

// and actually sort the posts in our query
usort($myquery->posts, 'menu_order_sort');

So now we have our own query object, and the $myquery->posts is sorted according to our custom menu_order_sort function. The only tricky part now, is that we must construct our loop using our custom query object:

while($myquery->have_posts()) : $myquery->the_post();
  ?>
    <div><a id="post_id_<?php the_ID(); ?>" class="nb" href="<?php the_permalink(); ?>" rel="nofollow noreferrer noopener"><?php the_title(); ?></a> Post ID: <?php the_ID(); ?>
    </div>
  <?php

endwhile;
wp_reset_postdata();

Obviously, you’d fix up the loop template code there.

I was hoping to find a solution that didn’t require the use of a custom query object, perhaps by using query_posts() and replacing the posts propery on the global $wp_query, but I just couldn’t get it to work right. With a little more time to work on it, that might have been doable.

Anyhow, see if that will get you where you need to go?

Method 2

You can try this:

add_filter('posts_orderby', 'enforce_specific_order');
$posts = get_posts($args);
remove_filter( current_filter(), __FUNCTION__ );

function enforce_specific_order($orderby) {
    global $wpdb;
    return "FIND_IN_SET(".$wpdb->posts.".ID, '1,3,8,4,12') ASC";
}

Method 3

I think this is the fastest way to return the results of a get_posts in a defined order. And besides that, it’s a native solution, without hacks

<?php

$posts_order = array('1,3,8,4,12');
$args = array(
    'post__in' => $posts_order,
    'orderby' => 'post__in'
); 
get_posts( $args ); 

?> 

Method 4

As of WordPress 3.5, this feature is now in core. You can explicitly order posts using the “post__in” parameter. http://core.trac.wordpress.org/ticket/13729

Method 5

How about just clearing the orderby with a filter? Right before you query your posts, put in:

add_filter('posts_orderby', '__return_false');

Then, after your loop is done:

remove_filter('posts_orderby', '__return_false');

The reason for removing the filter again is in case you have other loops on the page (such as from widgets) that will need their normal explicit ordering.


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Comment