Take the 2-minute tour ×
WordPress Development Stack Exchange is a question and answer site for WordPress developers and administrators. It's 100% free, no registration required.

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

share|improve this question
    
@Dalton, is a post order plugin out of the question? You could then use 'orderby' => 'menu_order', 'order' => 'ASC' –  eileencodes Mar 3 '11 at 18:12
    
In this case that wouldn't work - I'm actually returning attachments, each with different parents, so menu_order becomes irrelevant. Many thanks for the response! –  Dalton Mar 3 '11 at 18:20
    
@Dalton, perhaps I am misunderstanding your problem, but in the media uploader box, if you go to "gallery" you can reorder images by assigning them numbers and then again as suggested above use 'orderby' => 'menu_order, 'order' => 'ASC' –  eileencodes Mar 3 '11 at 18:43
    
Just a sidenote: You can use multiple arguments with orderby separated by space: &order_by=date ID for ex. –  kaiser Mar 3 '11 at 20:57
    
@eileen.carpenter: The problem in my case is that each attachment could theoretically have a different parent, making menu_order irrelevant. Each of the attachment IDs in my query could potentially have the same menu order. I'm passing the included posts via a shortcode, so I need to return the attachments in the order given. –  Dalton Mar 3 '11 at 23:02

5 Answers 5

up vote 6 down vote accepted

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?

share|improve this answer
2  
Bouy...looks a bit overkillish and doesn't work with pagination (as your sorting after querying), but +1 for a great effort to solve it without injecting something on the SQL level. –  wyrfel Mar 4 '11 at 18:28
    
Amazing! That definitely works for posts and answers the original question. There's just one hitch, which is that I need to return attachments, not posts. I didn't realize that WP-Query() doesn't work with attachments, I've been using get_posts() ... is there any easy way to get this to work with get_posts()? I'm going to keep working on it and see what I can come up with. –  Dalton Mar 4 '11 at 18:42
    
Updated the code to fetch attachments. :) With attachments, you have to specify post_status as 'any', because they normally use status inherit instead of publish, to reflect their parent's state. –  Dougal Campbell Mar 4 '11 at 19:24
    
Oh, and for future folks reading this, please note @wyrfel's comment: this will not work well if you need to paginate results, since the sorting doesn't happen within MySQL. –  Dougal Campbell Mar 4 '11 at 19:41
    
@Dougal: Many thanks, your changes did the trick. Fortunately I am not paging these results so I am 100% set. –  Dalton Mar 4 '11 at 20:33

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 ); 

?> 
share|improve this answer
    
Please add an explanation to your answer: why could that solve the problem? –  toscho Aug 3 '13 at 21:53
    
It's a native solution, without hacks –  PabloKarzin Aug 5 '13 at 15:01
    
Explain in your answer what is the diffenerece to the accepted answer which is using post__in too. –  toscho Aug 5 '13 at 16:39
1  
On the accepted answer, @dougal suggested to create two functions: set_include_order() and menu_order_sort(). There is no need for that. And besides, he suggested to use WP_Query instead of get_posts, as the question's author wanted. And post__in only works if you include the 'orderby' parameter too, like i suggested –  PabloKarzin Aug 5 '13 at 18:26
1  
great answer - down vote wrong - 3.6.1 - simple and easy - Thanks! –  Q Studio Oct 17 '13 at 17:32

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

share|improve this answer

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";
}
share|improve this answer
    
Thank you, that looks sharp, I'll give it a shot. –  Dalton Mar 4 '11 at 14:03
    
Works Perfect! Thanks! –  Sisir Jul 7 '12 at 15:38

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.

share|improve this answer
    
Dougal: There's actually an orderby=none option, but that returns the natural order of the posts in the database, essentially orderby ID. What I'm after is a way to return my posts in the order specified in the query. It's not supported by WP_Query, so I guess I'm looking for a workaround or hack. –  Dalton Mar 3 '11 at 23:09
    
Well, phooey. I figured that an 'include' query would return the results in the order given if you eliminated the orderby. But now that I'm thinking about it, it makes sense that MySQL would return the results in natural order (it's trying to be efficient). –  Dougal Campbell Mar 4 '11 at 14:18
3  
I was about to delete this answer of mine, but I thought it might be useful to keep around to document the database-natural-order behavior when you eliminate the orderby. –  Dougal Campbell Mar 4 '11 at 19:45

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.