Ordenar posts por meta_value_num: o preço em ordem certa no WP_Query

A vitrine ordenada por preço mostra 1200 logo antes de 89, e não é bug do tema. O valor mora na wp_postmeta, cuja coluna meta_value é longtext, e texto ordena caractere por caractere: “1200” vem antes de “89” pelo mesmo motivo que “ab” vem antes de “b”. A correção é uma palavra: troque meta_value por meta_value_num no orderby.

A consulta que ordena certo

<?php
$q = new WP_Query( [
    'post_type'      => 'produto',
    'posts_per_page' => 12,
    'meta_key'       => 'preco',
    'orderby'        => 'meta_value_num',
    'order'          => 'ASC',
] );

O meta_key é obrigatório aqui, senão o WordPress não sabe qual linha da postmeta juntar. Por baixo, o WP_Query 6.9 monta o ORDER BY como meta_value+0, o que joga a string para número no próprio MySQL 8. Efeito colateral: esse meta_key gera um INNER JOIN, então produto sem o campo preco some da lista.

Trazendo quem não tem o campo

<?php
$q = new WP_Query( [
    'post_type'      => 'produto',
    'posts_per_page' => 12,
    'meta_query'     => [
        'relation'  => 'OR',
        'com_preco' => [
            'key'     => 'preco',
            'type'    => 'DECIMAL(10,2)',
            'compare' => 'EXISTS',
        ],
        'sem_preco' => [
            'key'     => 'preco',
            'compare' => 'NOT EXISTS',
        ],
    ],
    'orderby'        => [ 'com_preco' => 'ASC' ],
] );

Dando nome à cláusula, o orderby aceita esse nome direto. E aqui mora a pegadinha que me pegou: ordenando por cláusula nomeada, o WordPress usa CAST(meta_value AS ...) com o type que você declarou — e NUMERIC vira SIGNED lá dentro, que corta os centavos. Com preço, peça DECIMAL(10,2); o 19,90 e o 19,99 dependem disso.

Quem cai no ramo NOT EXISTS entra com valor nulo e, em ASC, o MySQL 8 coloca nulo na frente de tudo. Se esses tiverem que ficar no fim da vitrine, ordene DESC ou grave zero no campo na hora de salvar o produto. E confira o valor cru na tabela antes de caçar plugin: campo gravado com R$ na frente ou vírgula no lugar do ponto continua saindo torto, porque a conversão para no primeiro caractere que não é número.

Dúvidas? Faça um comentário logo abaixo ou envie uma mensagem clicando aqui.

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *