Se você olhar para o método get_posts de WP_Query, é fácil depurar o sql enviando a variável passada para o filtro posts_request
.
// Debug the WP_Query sql
add_filter( 'posts_request', function( $sql ) {
echo "<pre>";
var_dump( $sql );
echo "</pre>";
return $sql;
} );
O resultado deve ser algo como isto:
string(426) "SELECT SQL_CALC_FOUND_ROWS wp_posts.* FROM wp_posts INNER JOIN wp_postmeta ON ( wp_posts.ID = wp_postmeta.post_id ) WHERE 1=1 AND wp_posts.post_type = 'post' AND (wp_posts.post_status = 'publish' OR wp_posts.post_status = 'private') AND (
wp_postmeta.meta_key = 'cat1'
OR
wp_postmeta.meta_key = 'cat2'
OR
wp_postmeta.meta_key = 'cat3'
) GROUP BY wp_posts.ID ORDER BY wp_postmeta.meta_value+0 DESC LIMIT 0, 10"
A consulta pode parecer correta no início, mas não é. Ele usa o meta_value
da primeira linha na junção interna para a cláusula ORDER BY
. Nesse caso, isso é 'cat1'
.
Para alterar esse comportamento, podemos usar o gancho posts_orderby
para alterar a instrução ORDER BY
e usar a função MAX
mysql para que use o valor mais alto de todos os meta_values em vez de apenas a primeira linha.
// Filter to alter the orderby sql
function nvdGMCWh_alter_posts_orderby( $sql ) {
return "MAX( $wpdb->postmeta.meta_value+0 ) DESC";
} // - alter_posts_orderby
// Register the filter
add_filter( 'posts_orderby', 'nvdGMCWh_alter_posts_orderby' );
// The WP_Query
$res = new WP_Query;
$args = [
'order' => 'DESC',
'orderby' => 'meta_value_num',
'meta_query' => [
'relation' => 'OR',
['key' => 'cat1', 'type' => 'numeric'],
['key' => 'cat2', 'type' => 'numeric'],
['key' => 'cat3', 'type' => 'numeric'],
],
]; // - $args
// Execute the query and get the results
$result = $res->query( $args );
// Clean up the filter so it won't affect later queries
remove_filter( 'posts_orderby', 'nvdGMCWh_alter_posts_orderby' );
// Loop through the results
foreach ( $result as $post ) {
// ... code here
} // - foreach