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' );
Answers:
Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.
Method 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' );
}
});
Method 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?
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0