The problem is that WordPress adds an INNER JOIN
to the wp_postmeta
table as soon as you mention meta_key
in your conditions. One way around the problem is to add a filter on the order by
clause, something like this:
function so_orderby_priority($original_orderby_statement) {
global $wpdb;
return "(SELECT $wpdb->postmeta.meta_value
FROM $wpdb->postmeta
WHERE $wpdb->posts.ID = $wpdb->postmeta.post_id
AND $wpdb->postmeta.meta_key = 'priority') ASC";
}
add_filter('posts_orderby', 'so_orderby_priority');
query_posts(
array(
'tag' => $pagetag,
'paged' => get_query_var('paged')
)
);
remove_filter('posts_orderby', 'so_orderby_priority');
Note MySQL sorts NULLs first - if you want them sorted last, try something like this (assuming all your priorities come before ZZZZZ alphabetically):
function so_orderby_priority($original_orderby_statement) {
global $wpdb;
return "IFNULL(
(SELECT $wpdb->postmeta.meta_value
FROM $wpdb->postmeta
WHERE $wpdb->posts.ID = $wpdb->postmeta.post_id
AND $wpdb->postmeta.meta_key = 'priority'),
'ZZZZZ') ASC";
}
Edit
Here's a bit more explanation, which assumes you understand SQL at least a bit.
Your original query_posts
resulted in the following query running against the database:
SELECT wp_posts.*
FROM wp_posts
INNER JOIN wp_term_relationships ON ( wp_posts.id = wp_term_relationships.object_id )
INNER JOIN wp_postmeta ON ( wp_posts.id = wp_postmeta.post_id )
WHERE 1 = 1
AND ( wp_term_relationships.term_taxonomy_id IN ( 3 ) )
AND wp_posts.post_type = 'post'
AND ( wp_posts.post_status = 'publish'
OR wp_posts.post_status = 'private' )
AND ( wp_postmeta.meta_key = 'priority' )
GROUP BY wp_posts.id
ORDER BY wp_postmeta.meta_value ASC
LIMIT 0, 10;
That INNER JOIN wp_postmeta
is what removed any posts without a priority from your results.
Removing the meta_*
related conditions from your query_posts
:
query_posts(
array(
'tag' => $pagetag,
'paged' => get_query_var('paged')
)
);
solved that problem, but the sort order is still wrong. The new SQL is
SELECT wp_posts.*
FROM wp_posts
INNER JOIN wp_term_relationships ON ( wp_posts.id = wp_term_relationships.object_id )
WHERE 1 = 1
AND ( wp_term_relationships.term_taxonomy_id IN ( 3 ) )
AND wp_posts.post_type = 'post'
AND ( wp_posts.post_status = 'publish'
OR wp_posts.post_status = 'private' )
GROUP BY wp_posts.id
ORDER BY wp_posts.post_date DESC
LIMIT 0, 10;
The posts_orderby
filter allows us to change the ORDER BY
clause: wp_posts.post_date DESC
gets replaced by what the filter returns. The final SQL becomes:
SELECT wp_posts.*
FROM wp_posts
INNER JOIN wp_term_relationships ON ( wp_posts.id = wp_term_relationships.object_id )
WHERE 1 = 1
AND ( wp_term_relationships.term_taxonomy_id IN ( 3 ) )
AND wp_posts.post_type = 'post'
AND ( wp_posts.post_status = 'publish'
OR wp_posts.post_status = 'private' )
GROUP BY wp_posts.id
ORDER BY (SELECT wp_postmeta.meta_value
FROM wp_postmeta
WHERE wp_posts.id = wp_postmeta.post_id
AND wp_postmeta.meta_key = 'priority') ASC
LIMIT 0, 10
which does what you're after.