Classifique o valor do meta, mas inclua posts que não possuem um

Eu tenho modificado a pesquisa WP criada usando o filtro pre_get_posts , permitindo ao usuário classificar as postagens (incluindo um conjunto de tipos de postagem personalizados) por diferentes campos.

O problema que estou tendo é que, quando eu digo ao WP para classificar por um valor meta, ele excluirá todas as postagens que não possuem esse valor de meta. Isso faz com que o número de resultados seja alterado se você alterar a sorting de “Preço” para “Data” porque “Publicações” não têm “Preço” definido, mas “Itens” fazem.

Isso não é o que eu quero, então eu gostaria de saber se existe uma maneira de include TODAS as postagens – mesmo aquelas que não têm o valor meta que eu estou ordenando – e colocamos a nossa sem o valor final.

Eu sei como classificar em mais de um campo, mas isso não ajuda.

obrigado

Parece que não sou o único com esta pergunta: maneira de include postagens com e sem determinadas meta_key em args para wp_query? mas não há solução lá.

Atualizar

Eu tentei a resposta, mas não tenho certeza se eu entendi corretamente, aqui está o que tenho agora:

 set('meta_query', array(array( 'key' => 'item_price', 'value' => '', 'compare' => 'NOT EXISTS' ))); $qry->set('orderby', 'meta_value date'); # Sorting works with meta_value as well as meta_value_num - I've tried both $qry->set('order', 'ASC DESC'); $qry->set('meta_key', 'item_price'); } 

O meta valor é um número (é usado para armazenar um preço como o nome sugere)

Atualização 2

Eu comentei o pedido e tudo o que tenho agora é o seguinte:

 set('meta_query', array(array( 'key' => 'item_price', 'value' => '', 'compare' => 'NOT EXISTS' ))); 

Com este código, a consulta parece retornar todas as postagens que não possuem a chave item_price e nenhuma das postagens que a tenham. IE o problema agora está revertido.

Se eu adicionar o código de pedido, também obtendo 0 resultados.

Editar: … três anos depois … : PI teve esse problema novamente. Eu tentei todas as respostas dadas e nenhum trabalho. Não tenho certeza por que algumas pessoas parecem pensar que funcionam, mas não funcionam pelo menos para mim.

A solução com a qual acabei é usar o filtro save_post – certificando-se de que todas as postagens tenham o campo personalizado que desejo classificar. É um pouco irritante que eu tenha que fazê-lo, mas enquanto você faz isso no começo, provavelmente não terá problemas.

Nesse caso, eu estava construindo um “contador de vistas” em posts e queria que os usuários pudessem classificar as postagens mais lidas. Mais uma vez, mensagens que nunca foram visualizadas (acho que isso é bastante improvável – mas ainda) desapareceu ao classificar a contagem. Eu adicionei esse bit de código para garantir que todas as postagens tenham uma contagem de visualizações:

 add_action('save_post', function ($postId) { add_post_meta($postId, '_sleek_view_count', 0, true); }); 

Solutions Collecting From Web of "Classifique o valor do meta, mas inclua posts que não possuem um"

Existem duas soluções possíveis para isso:

1. Todas as postagens têm meta

A melhor solução que encontrei aqui é dar ao restante das postagens / produtos um preço de item de 0. Você pode fazer isso manualmente, ou encaminhar todas as postagens e se o preço estiver vazio e atualizá-lo.

Para tornar isso gerenciável no futuro, você pode se conectar ao save_post e dar-lhes um valor quando eles são adicionados pela primeira vez (somente se estiver em branco).

2. Consultas múltiplas

Você pode executar a primeira consulta como está fazendo e armazenar as IDs das postagens retornadas. Em seguida, você pode executar outra consulta para todas as postagens e a data da ordem, excluindo o retorno das ID da primeira consulta.

Você pode imprimir os dois resultados separadamente e você obterá os resultados desejados.

Este método retornará todas as postagens, incluindo aquelas com e sem a meta_key solicitada, mas fará coisas estranhas ao fazer o pedido.

 add_action('pre_get_posts', 'my_stuff'); function my_stuff ($qry) { $qry->set( 'meta_query', array( 'relation' => 'OR', # Matches to this meta_query should be added to those matching the 'meta_key' query array( 'key' => 'item_price', 'value' => 'bug #23268', 'compare' => 'NOT EXISTS' ) ) ); $qry->set('orderby', 'meta_value date'); # Sorting works with meta_value as well as meta_value_num - I've tried both $qry->set('order', 'ASC DESC'); $qry->set('meta_key', 'item_price'); } 

Eu achei isso mexendo com todas as respostas diferentes a esta questão e analisando o SQL gerado através de tentativa e erro. Parece que a configuração da array('meta_query' => array('relation' => 'OR')) produz um LEFT JOIN apropriado em vez de INNER JOIN que é necessário include posts que faltam os metadados. Especificar o NOT EXISTS impede a cláusula WHERE de filtrar posts sem o meta-campo. Para este WP_Query particular, o SQL gerado é (indentation / newlines added):

 SELECT SQL_CALC_FOUND_ROWS wp_posts.ID FROM wp_posts INNER JOIN wp_term_relationships ON (wp_posts.ID = wp_term_relationships.object_id) INNER JOIN wp_postmeta ON wp_posts.ID = wp_postmeta.post_id LEFT JOIN wp_postmeta AS mt1 ON (wp_posts.ID = mt1.post_id AND mt1.meta_key = 'item_price') WHERE 1=1 AND ( wp_term_relationships.term_taxonomy_id IN (2) ) AND wp_posts.post_type = 'post' AND (wp_posts.post_status = 'publish' OR wp_posts.post_status = 'private') AND (wp_postmeta.meta_key = 'item_price' -- Oh look, here we give SQL permission to choose a random -- row from wp_postmeta when this particular post is missing -- 'item_price': OR mt1.post_id IS NULL ) GROUP BY wp_posts.ID ORDER BY wp_postmeta.meta_value,wp_posts.post_date DESC LIMIT 0, 10 

O resultado é uma lista de todas as postagens com meta_value de item_price e os item_price falta item_price . Todas as postagens com item_price serão ordenadas corretamente uma em relação à outra, mas as postagens perdidas item_price usarão algum outro valor meta random (digamos, _edit_last que parece ser 1 bastante freqüência no meu database ou em alguns outros metadados wordpress internos que são completamente arbitrários ) por seu wp_postmeta.meta_value na cláusula ORDER BY . Então, enquanto este método está próximo e pode parecer funcionar para determinados dados, ele está quebrado. Então, tudo o que posso dizer é que, se seus valores item_price não item_price com os campos meta randoms, o MySQL escolhe as postagens perdidas item_price , isso pode funcionar bem para você. Se tudo o que você precisa é uma garantia de que suas postagens com item_price estão corretamente ordenadas entre si sem considerar o pedido de outras postagens, pode ser OK. Mas acho que isso é apenas uma deficiência na wordpress. Por favor, corrija-me, espero que eu esteja errado e há uma maneira de abordar isso ;-).

Parece que para o INNER JOIN wp_postmeta , o MySQL está escolhendo uma linha aleatória entre múltiplas linhas pós- postmeta associadas à postagem quando a meta_key está ausente da publicação dada. Do ponto de vista de SQL, precisamos descobrir como dizer o wordpress para o output ORDER BY mt1.meta_value . Esta coluna está devidamente NULL quando a nossa meta_key solicitada está faltando, ao contrário de wp_postmeta.meta_value . Se pudéssemos fazer isso, o SQL classificaria esses NULL (inputs ausentes) antes de qualquer outro valor, dando-nos uma ordem bem definida: primeiro venha todas as postagens faltando o campo postmeta específico, em segundo lugar, as postagens que têm o campo. Mas esse é todo o problema: 'orderby' => 'meta_value' só pode se referir a 'meta_key' => 'item_price' e o wp_postmeta é sempre um INNER JOIN vez de sempre um LEFT JOIN , que significa wp_postmeta.meta_value e wp_postmeta.meta_key nunca pode ser NULL .

Então, eu acho que devo dizer que isso não é possível com o WP_Query incorporado da wordpress, como agora está documentado (no wordpress-3.9.1). Bother. Então, se você realmente precisa que isso funcione corretamente, você provavelmente precisa se conectar ao wordpress em outro lugar e modificar o SQL gerado diretamente .

Acho que tenho uma solução.

Você pode usar dois meta_key s, um que todas as postagens têm (like "_thumbnail_id") e a meta_key você deseja usar como filtro.

Então, seus args:

 $qry->set( 'meta_query', array( 'relation' => 'OR', array( 'key' => 'item_price', 'value' => '', 'compare' => 'EXISTS' ), array( 'key' => 'item_price', 'value' => '', 'compare' => 'EXISTS' ) ) ); $qry->set('orderby', 'meta_value date'); # Sorting works with meta_value as well as meta_value_num - I've tried both $qry->set('order', 'ASC DESC'); $qry->set('meta_key', 'item_price'); 

Se for adequado, você pode adicionar um meta valor padrão cada vez que uma publicação é salva ou atualizada, se o valor meta não existe.

 function addDefaultMetaValue($post_id) { add_post_meta($post_id, 'item_price', 0, true); } add_action('save_post', 'addDefaultMetaValue'); 

Se você estiver usando um tipo de postagem personalizado, substitua o add_action('save_post', 'addDefaultMetaValue'); por add_action('save_post_{post_type}', 'addDefaultMetaValue'); eg add_action('save_post_product', 'addDefaultMetaValue');

Existe um possível valor de orderby de meta_value para isso.

 $query = new WP_Query( array ( 'meta_key' => 'your_keys_name', 'orderby' => 'meta_value', 'order' => 'DESC', 'meta_query' => array( array( 'key' => 'your_meta_key', 'value' => '', 'compare' => 'NOT EXISTS', // 'type' => 'CHAR', ) ) ) ); 

Se você obteve valores numéricos, use apenas meta_value_num vez disso.

Disclaimer: Isto não é testado, mas deve funcionar. O ponto é que você precisa especificar seus valores de meta_key e key . Além disso, você não pode comparar os valores não existentes, o que deve possibilitar a consulta de ambos os tipos de postagens. É algum tipo de hack-ish, mas enquanto ele funciona …

Eu acho que o que o @kaiser estava tentando fazer era dizer à consulta que devolvesse todas as postagens que possuíam essa meta-chave, aplicando uma espécie de manequim onde a condição para não filtrar nenhuma dessas postagens. Então, se você conhece todos os valores que seus campos personalizados podem tomar são x, y, z você poderia dizer “ONDE meta_key IN (x, y, z) “, mas a idéia é que você pode evitar esse problema todos juntos dizendo ! = (‘ ‘) :

 $query = new WP_Query( array ( 'orderby' => 'meta_value_num', 'order' => 'DESC', 'meta_query' => array( array( 'key' => 'item_price', 'value' => '', 'compare' => '!=', ) ) ) ); 

Também não testado, mas parece que vale a pena tentar :-).

Eu acabei por resolver isso com um pouco de hack (IMHO), mas fez o trabalho para mim no meu caso.

Você pode se conectar aos filtros posts_join_paged e posts_orderby para atualizar as cordas de junit e ordem. Isso permitirá que você ordene o que quiser, desde que você se junte primeiro ao invés de WP_Query assumindo que o campo deve existir para essa publicação específica. Você pode então remover a meta_key , orderby e `order from your WP_Query args.

Abaixo está um exemplo. No topo de cada function, tive que escaping para certos casos, uma vez que irá adicionar isso a tudo que usa WP_Query. Talvez seja necessário modificar isso para atender às suas necessidades específicas.

A documentação sobre esses dois filtros está tristemente faltando, então … boa sorte! 🙂

 add_filter('posts_join_paged', 'edit_join', 999, 2); add_filter('posts_orderby', 'edit_orderby', 999, 2); /** * Edit join * * @param string $join_paged_statement * @param WP_Query $wp_query * @return string */ function edit_join($join_paged_statement, $wp_query) { global $wpdb; if ( !isset($wp_query->query) || $wp_query->is_page || $wp_query->is_admin || (isset($wp_query->query['post_type']) && $wp_query->query['post_type'] != 'my_custom_post_type') ) { return $join_paged_statement; } $join_to_add = " LEFT JOIN {$wpdb->prefix}postmeta AS my_custom_meta_key ON ({$wpdb->prefix}posts.ID = my_custom_meta_key.post_id AND my_custom_meta_key.meta_key = 'my_custom_meta_key') "; // Only add if it's not already in there if (strpos($join_paged_statement, $join_to_add) === false) { $join_paged_statement = $join_paged_statement . $join_to_add; } return $join_paged_statement; } /** * Edit orderby * * @param string $orderby_statement * @param WP_Query $wp_query * @return string */ function edit_orderby($orderby_statement, $wp_query) { if ( !isset($wp_query->query) || $wp_query->is_page || $wp_query->is_admin || (isset($wp_query->query['post_type']) && $wp_query->query['post_type'] != 'my_custom_post_type') ) { return $orderby_statement; } $orderby_statement = "my_custom_meta_key.meta_value DESC"; return $orderby_statement; }