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