Wordpress Avançado

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.

Post Recentes

Block pattern não aparece no inseridor: registre pelo arquivo em /patterns

Padrão registrado no functions.php que não aparece na aba Padrões. O caminho curto do WordPress 6.9: um arquivo PHP na…

7 horas atrás

Tamanho das tabelas MySQL 8: a query que acha o vilão do disco

Disco lotando por causa do banco? Uma query no information_schema mostra qual tabela cresceu, quanto é dado, quanto é índice…

13 horas atrás

CodeIgniter 3 PHP 8.3: os erros fatais que aparecem logo no boot

Sistema em CodeIgniter 3.1.13 morre no primeiro request depois do upgrade. Os três pontos que quebram no PHP 8.3 e…

2 dias atrás

rsync do pub/media no Magento 2: copiar as imagens sem levar o cache

Metade do seu pub/media é miniatura descartável. Veja o rsync com os excludes certos e o comando que reconstrói o…

2 dias atrás

mu-plugins no WordPress: onde colocar snippets em vez do functions.php

Tire os snippets do functions.php e coloque num must-use plugin: carrega sempre, sobrevive à atualização do tema e ninguém desativa…

3 dias atrás

preg_replace_callback no PHP: substituir cada match com uma função

Quando o valor novo depende do que a regex achou, preg_replace não resolve. Exemplo real aplicando 10% de desconto em…

4 dias atrás