Leave a Reply

2 comments

  1. 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' );
      }
    });
    
  2. 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?