Orderby meta_value retorna apenas postagens que possuem meta_key


10

Eu tenho o seguinte wp_query:

$args = array(
    'post_type' => 'news',
    'orderby' => 'meta_key',
    'order' => 'ASC',
    'meta_key'=>'custom_author_name',
    'post_per_page'=>-1
);

$query = new WP_Query($args);

echo $query->found_posts;

eco = 10 resultados porque existem apenas 10 newspostagens com a meta_key = custom_author_name. Mas existem centenas de newspostagens que não têm uma linha post_meta com essa meta_key específica. Observe que não há meta_query envolvido. Nenhum meta_value é atribuído, porque estou apenas tentando classificar as postagens por meta_key e não filtrar por meta_value.

O pedido não deve selecionar todas as postagens? e apenas encomendá-los?

Se sim, por que o resultado é filtrado? Se a meta_key não for encontrada, por que não usar apenas uma string vazia ou uma combinação de todas?

Se não, por que não?

Se eu inserir uma meta_key em cada post de notícias (mesmo que seja uma sequência vazia), obterá o resultado esperado. Mas isso parece um monte de linhas da tabela que não precisam estar lá.

Respostas:


10

Como afirmado na resposta de @ ambroseya, é suposto funcionar dessa maneira. Depois de declarar uma meta consulta, mesmo se você não estiver procurando por um valor específico, ele somente consultará as postagens com essa meta chave declarada. Se você deseja incluir todas as postagens, classifique-as pela meta-chave, use o seguinte código:

$args = array(
    'post_type' => 'news',
    'orderby' => 'meta_value',
    'order' => 'ASC',
    'meta_query' => array(
        'relation' => 'OR',
        array( 
            'key'=>'custom_author_name',
            'compare' => 'EXISTS'           
        ),
        array( 
            'key'=>'custom_author_name',
            'compare' => 'NOT EXISTS'           
        )
    ),
    'posts_per_page'=>-1
);

$query = new WP_Query($args);

echo $query->found_posts;

O que isso faz é usar uma meta consulta avançada que procure postagens que tenham e que não tenham essa meta chave declarada. Como o EXISTSprimeiro é o primeiro, quando você classifica por meta_value, ele usa a primeira consulta.


3
Isso não mudou a ordem para mim; quando eu usei 'orderby' => 'meta_value', mudou a ordem, mas não teve nada a ver com o meta campo real.
Jake

3

Eu tentei aplicar a resposta de @Manny Fleurmond e como @Jake eu não poderia obtê-lo a trabalhar mesmo depois de corrigir o erro de digitação que 'orderby' => 'meta_key'deve ser 'orderby' => 'meta_value'. (E, para completar, deve ser 'posts_per_page'não 'post_per_page', mas que não afeta a questão que está sendo olhado.)

Se você olhar para a consulta SQL realmente gerada pela resposta de @Manny Fleurmond (após corrigir os erros de digitação), é isso que você obtém:

SELECT   wp_{prefix}_posts.* FROM wp_{prefix}_posts
LEFT JOIN wp_{prefix}_postmeta ON (wp_{prefix}_posts.ID = wp_{prefix}_postmeta.post_id AND wp_{prefix}_postmeta.meta_key = 'custom_author_name' )
LEFT JOIN wp_{prefix}_postmeta AS mt1 ON ( wp_{prefix}_posts.ID = mt1.post_id )
WHERE 1=1  AND ( 
    wp_{prefix}_postmeta.post_id IS NULL 
    OR 
    mt1.meta_key = 'custom_author_name'
) AND wp_{prefix}_posts.post_type = 'news' AND
(wp_{prefix}_posts.post_status = 'publish' OR wp_{prefix}_posts.post_author = 1 AND wp_{prefix}_posts.post_status = 'private')
GROUP BY wp_{prefix}_posts.ID ORDER BY wp_{prefix}_postmeta.meta_value ASC

Isso ilustra a maneira como o WP está analisando os vars de consulta: está criando uma tabela para cada cláusula meta_query e, em seguida, descobrindo como se juntar a eles e o que pedir. A ordem funcionaria bem se você estivesse usando apenas uma única cláusula com 'compare' => 'EXISTS', mas a junção da segunda 'compare' => 'NOT EXISTS'cláusula com OR (como devemos) atrapalha a ordem. O resultado é que LEFT JOIN é usado para unir a primeira cláusula / tabela e a segunda cláusula / tabela - e a maneira como o WP reúne tudo significa que a tabela criada usando 'compare' => 'EXISTS'está realmente sendo preenchida com meta_values ​​de QUALQUER campo personalizado, não apenas o 'custom_author_name'no qual estamos interessados. Portanto, acho que a ordenação por essa cláusula / tabela fornecerá apenas os resultados desejados se o tipo de post específico de 'news' tiver apenas um único campo personalizado.

A solução que funcionou para minha situação foi pedir pela outra cláusula / tabela - a NÃO EXISTE. Aparentemente contra-intuitivo, eu sei, mas devido à maneira como o WP analisa os vars de consulta, é nesta tabela que meta_valueé preenchida apenas pelo campo personalizado que buscamos.

(A única maneira de descobrir isso foi executando o equivalente a essa consulta para o meu caso:

SELECT   wp_{prefix}_posts.ID, wp_{prefix}_postmeta.meta_value, mt1.meta_value FROM wp_{prefix}_posts
LEFT JOIN wp_{prefix}_postmeta ON (wp_{prefix}_posts.ID = wp_{prefix}_postmeta.post_id AND wp_{prefix}_postmeta.meta_key = 'custom_author_name' )
LEFT JOIN wp_{prefix}_postmeta AS mt1 ON ( wp_{prefix}_posts.ID = mt1.post_id )
WHERE 1=1  AND ( 
    wp_{prefix}_postmeta.post_id IS NULL 
    OR 
    mt1.meta_key = 'custom_author_name'
) AND wp_{prefix}_posts.post_type = 'news' AND
(wp_{prefix}_posts.post_status = 'publish' OR wp_{prefix}_posts.post_author = 1 AND wp_{prefix}_posts.post_status = 'private')
ORDER BY wp_{prefix}_postmeta.meta_value ASC

Tudo o que fiz foi alterar as colunas exibidas e remover a cláusula GROUP BY. Isso então me mostrou o que estava acontecendo - que a coluna postmeta.meta_value estava obtendo valores de todas as meta_keys, enquanto a coluna mt1.meta_value estava obtendo apenas meta_values ​​do campo personalizado de notícias.)

A solução

Como @Manny Fleurmond diz, é a primeira cláusula que é usada para o pedido, então a resposta é apenas trocar as cláusulas, dando o seguinte:

$args = array(
    'post_type' => 'news',
    'orderby' => 'meta_value',
    'order' => 'ASC',
    'meta_query' => array(
        'relation' => 'OR',
        array( 
            'key' => 'custom_author_name',
            'compare' => 'NOT EXISTS'           
        ),
        array( 
            'key' => 'custom_author_name',
            'compare' => 'EXISTS'           
        )
    ),
    'posts_per_page' => -1
);

$query = new WP_Query($args);

Como alternativa, você pode criar as cláusulas matrizes associativas e ordenar pela chave correspondente, da seguinte forma:

$args = array(
    'post_type' => 'news',
    'orderby' => 'not_exists_clause',
    'order' => 'ASC',
    'meta_query' => array(
        'relation' => 'OR',
        'exists_clause' => array( 
            'key' => 'custom_author_name',
            'compare' => 'EXISTS'           
        ),
        'not_exists_clause' => array( 
            'key' => 'custom_author_name',
            'compare' => 'NOT EXISTS'           
        )
    ),
    'posts_per_page' => -1
);

$query = new WP_Query($args);

Vale ressaltar que, se a meta-chave custom_author_nameestiver configurada e depois desabilitada, meta_keyela responderá EXISTSe o efeito será que essas bolhas serão exibidas ao lado das postagens que possuem a custom_author_name. No meu caso eu tenho uma caixa de seleção, então eu estou usando "value" => "1", em vez de EXISTS, mas cordas terá uma abordagem diferente.
djb

1

É assim que funciona.

Se você quiser fazer isso sem adicionar linhas da tabela, precisará fazer duas consultas. Um com a meta_key que possui resultados limitados e o outro com a lista inteira; em seguida, use o PHP para comparar os dois resultados da consulta (possivelmente removendo os resultados da meta_key da outra consulta para remover duplicatas ou o que fizer sentido na sua configuração).


0

Infelizmente, não é assim que WP_Queryfunciona. Assim que você adiciona esse componente "meta", você cria um tipo de filtro. Despejo $query->requeste você verá o que quero dizer.

Segundo, WP_Querynão suporta a solicitação por uma meta- chave . Você pode solicitar por um valor meta para uma chave específica, mas não pela própria chave. Mais uma vez, despeje a consulta para ver o que quero dizer. Você notará que os componentes de "pedido" desaparecem se você tentar.

A maneira mais limpa de fazer isso funcionar, na minha opinião, são alguns filtros curtos:

function join_meta_wpse_188287($join) {
  remove_filter('posts_join','join_meta_wpse_188287');
  global $wpdb;
  return ' INNER JOIN '.$wpdb->postmeta.' ON ('.$wpdb->posts.'.ID = '.$wpdb->postmeta.'.post_id)';
}
add_filter('posts_join','join_meta_wpse_188287');

function orderby_meta_wpse_188287($orderby) {
  remove_filter('posts_orderby','orderby_meta_wpse_188287');
  global $wpdb;
  return $wpdb->postmeta.'.meta_key ASC';
}
add_filter('posts_orderby','orderby_meta_wpse_188287');

$args = array(
    'post_type' => 'news',
    'post_per_page'=>-1
);
$q = new WP_Query($args);
var_dump($q->request); // debug
var_dump(wp_list_pluck($q->posts,'post_title')); // debug
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.