8

I'm building the sorting functionality for a custom post type, and I have a custom meta value for "Featured" posts. This value is set when you tick the checkbox, otherwise it is not set.

Using the default orderby=meta_value with meta_key=featured makes it so that the screen only displays posts that have the meta key. If they don't, they don't even appear.

I want it so if it is not set, they do appear, but they appear last. I assume I need to use meta_query instead, but I couldn't get that to work either.

How do I allow empty, false, or non-existing meta keys in the WP_Query arguments?

My code is below. This is for sorting columns in the dashboard, so it is modifying the default WP Query args.

function featured_sortable_order( $vars ) {
  if ( isset($vars['orderby']) && $vars['orderby'] == 'featured' ) {

    $vars = array_merge( $vars, array(
      'meta_key' => 'featured',
      'orderby' => 'meta_value',
      'order'     => isset($vars['order']) ? $vars['order'] : 'asc',
    ) );

  }
  return $vars;
}
add_filter( 'request', 'featured_sortable_order' );
4
  • To clarify you are looking to have a "sticky" (featured) functionality? And you wish the sort to happen based on the meta_key value being present giving preference to the order? Commented Mar 21, 2014 at 20:30
  • I ran into this problem not too long ago - looked into maybe combining 2 queries together but in the end I just decided to go with 2 queries / 2 loops. That's the inefficient "get it done" solution though.
    – Howdy_McGee
    Commented Mar 21, 2014 at 20:39
  • 1
    @codearachnid Yes, a custom sticky feature although from the dashboard they are the same. All the sorting would do is either put all featured posts first, or last, but not exclude non-featured posts. Commented Mar 21, 2014 at 20:54
  • @Howdy_McGee I just hope I don't have to hack it. I'd like to learn the proper way :) Commented Mar 21, 2014 at 20:55

2 Answers 2

7

Completely edited after first publish

The problem is that to order for a meta value, WordPress need to 'meta_key' in query is set to something. But if you set 'meta_key' to something, then WordPress will add something like

AND ( wp_postmeta.meta_key = 'the_meta_key' )

to WHERE SQL clause; and something like

INNER JOIN wp_postmeta ON ( wp_posts.ID = wp_postmeta.post_id )

to join clauses. So query returns only the posts that has that meta query.

Once you are working on backend, and you call get_post_meta( $postid, 'featured', true) for every post to show the column, is not a great performance issue call it 2 times thanks to WordPress cache on get meta function.

So, the idea is get all the posts (without adding filter on meta key), then filter posts using 'posts_results' hook and order posts looking at the 'featured' meta key.

I'll remove the filter just after having used it.

add_filter( 'posts_results', 'order_by_featured', PHP_INT_MAX, 2 );

function order_by_featured ( $posts, $query ) {
  // run only on admin, on main query and only if 'orderby' is featured
  if ( is_admin() && $query->is_main_query() && $query->get('orderby') === 'featured' ) {
    // run once
    remove_filter( current_filter(), __FUNCTION__, PHP_INT_MAX, 2 );
    $nonfeatured = array();
    $featured = array();
    foreach ( $posts as $post ) {
      if ( get_post_meta( $post->ID, 'featured', TRUE ) ) {
        $featured[] = $post;
      } else {
        $nonfeatured[] = $post;
      }
    }
    $order = strtoupper( $query->get('order') ) === 'ASC' ? 'DESC' : 'ASC';
    // if order is ASC put featured at top, otherwise put featured at bottm
    $posts = ( $order === 'ASC' )
      ? array_merge( $nonfeatured, $featured )
      : array_merge( $featured, $nonfeatured );
  }
  return $posts;
}

In addition, I add a filter on 'pre_get_post' to use 'ASC' as default order if no order is set in the query:

add_action( 'pre_get_posts', function( $query ) {
  // if no order is set set order to ASC
  if (
    is_admin() && $query->is_main_query()
    && $query->get('orderby') === 'featured' 
    && $query->get('order') === ''
  ) {
    $query->set( 'order', 'ASC' );
  }
});
7
  • Wow that's a lot of code to get this to work! It's not quite right though (radleygh.com/images/chrome_2014-080-15-55-54-79.png). I'll have to debug this more next week, I'm leaving right now. Thanks though! (It looks like an edit was made while i was testing, it may be fixed, but I can't check yet!) Commented Mar 21, 2014 at 22:57
  • @RadGH I've completely edited the answer. Now it works without issues for me, however, once this is actually another answer...
    – gmazzap
    Commented Mar 22, 2014 at 0:11
  • In your opinion, do you think this is faster than running 2 queries separately? In multiple queries you are only getting what you need, then displaying them. In this method you are pulling all posts, then looping through each post to check if it's featured. Or do you think there isn't a performance difference?
    – Howdy_McGee
    Commented Mar 25, 2014 at 15:49
  • 2
    After some testing, posts_results only pulls the number of posts via posts_per_page parameter, so the above scenario would only work if you actually are displaying all posts on a single page. Is that correct or could I be implementing it wrong?
    – Howdy_McGee
    Commented Mar 26, 2014 at 19:36
  • 1
    @Howdy_McGee yes, this is intened to order a given set of posts based on be featured or not. I.E. you have a query that returns 20 posts and 5 are featured and 15 not. With my code you can have the featured at top (or at bottom) passing 'orderby' => 'featured'. That means if you have some post that are featured, but are not included in the query, you'll not get them.
    – gmazzap
    Commented Mar 26, 2014 at 19:56
-1

Why not giving all posts the meta attribute with a custom value?

I mean, you can set all posts to featured = no/false/0/low-numeric-valeu and then change to yes/true/1/high-numeric-values all those that should be featured and they will get ordered, won't they?

1
  • 2
    That would work and I did consider it. I just expected there to be support for missing meta keys. And this key applies to many post types. And there are already pages. I would have to fill the gaps when the plugin is activated, which I guess isn't a terrible idea since it is a custom meta key after all. It does feel a bit unecessary though. Commented Mar 28, 2014 at 16:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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