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

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.

Read More

Can anyone suggest a workaround for this behavior?

Cheers,
Dalton

Related posts

Leave a Reply

5 comments

  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(); ?>"><?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?

  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";
    }
    
  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 ); 
    
    ?> 
    
  4. 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.