Turns out, WordPress joins the postmeta table several times, depending
on the meta query arguments.
Yes that’s correct, and it’s also the reason why I said (in my other answer) that in an orderby
array, you should use the key of an array in the meta_query
which contains a direct item named/keyed key
(where the value is a meta key) — and if you don’t, then WordPress will not know which table join/alias should be used.
This means the
1C_quantity_total
meta key has to be referenced as
mt2.meta_key
, notwp_postmeta.meta_key
.
Yes, you’re absolutely right!
( And sorry, my bad, I revised my other answer several times and yet, I actually still forgot to correct the table alias 😅 )
Now, I don’t feel like hard-coding the table’s alias, mt2. What if the
alias changes in future versions of WordPress (e.g. mtB)? Also, I’m
not sure1C_quantity_total
will be in exactly the third joined
postmeta table in any circumstances. Is there a way to do it better?
Yes, there is: use WP_Meta_Query::get_clauses()
to get the correct alias used for each meta query clause. And with a WP_Query
instance, you can access the meta query class instance using the $meta_query
property, i.e. WP_Query::$meta_query
.
But remember to give a unique array key to your meta query clauses like the has_tdlrm_mp
and has_1C_quantity_total
:
$args = array(
...
'meta_query' => array(
array(
'relation' => 'OR',
'has_tdlrm_mp' => array(
'key' => 'tdlrm_mp',
'type' => 'NUMERIC',
),
'no_tdlrm_mp' => array(
'key' => 'tdlrm_mp',
'compare' => 'NOT EXISTS',
),
),
array(
'relation' => 'OR',
'has_1C_quantity_total' => array(
'key' => '1C_quantity_total',
'type' => 'NUMERIC',
),
'no_1C_quantity_total' => array(
'key' => '1C_quantity_total',
'compare' => 'NOT EXISTS',
),
)
),
...
);
So with that meta query, you can get the correct table alias like so:
$query = new WP_Query( $args );
$meta_clauses = $query->meta_query->get_clauses();
var_dump(
$meta_clauses['has_tdlrm_mp']['alias'],
$meta_clauses['has_1C_quantity_total']['alias'],
);
// Sample output: string(11) "wp_postmeta" string(3) "mt2"
And inside the posts_orderby
filter/callback, you can use the same $query->meta_query->get_clauses()
above, like so:
$meta_clauses = $query->meta_query->get_clauses();
$has_tdlrm_mp = $meta_clauses['has_tdlrm_mp']['alias'];
$has_1C_quantity_total = $meta_clauses['has_1C_quantity_total']['alias'];
return "
CASE {$has_tdlrm_mp}.meta_key
WHEN 'tdlrm_mp' THEN 1
ELSE 2
END ASC,
CASE {$has_tdlrm_mp}.meta_key
WHEN 'tdlrm_mp' THEN {$has_tdlrm_mp}.meta_value+0
END ASC,
CASE {$has_1C_quantity_total}.meta_key
WHEN '1C_quantity_total' THEN {$has_1C_quantity_total}.meta_value+0
END DESC
";