web-development-kb-pt.site

Usando WP_Query para consultar várias categorias com postagens limitadas por categoria?

Eu tenho 3 categorias com cada 15 posts, eu quero fazer uma consulta para o db trazendo apenas 5 primeiros posts para cada categoria, como eu posso fazer isso?

$q = new WP_Query(array( 'post__in' => array(2,4,8), 'posts_per_page' => **FIRST_5_OF_EACH_CAT** ));

Caso isso não seja possível, o que é mais eficiente, obtendo todas as postagens para a categoria pai e passando por elas ou criando 3 consultas diferentes?

10
Amit

O que você quer é possível, mas vai exigir que você se aprofunde no SQL que eu gostaria de evitar sempre que possível (não porque eu não saiba disso, eu sou um desenvolvedor de SQL avançado, mas porque em WordPress você quer usar a API sempre que possível para minimizar futuros problemas de compatibilidade relacionados a possíveis mudanças futuras na estrutura do banco de dados.)

SQL com um operador UNION é uma possibilidade

Para usar o SQL, o que você precisa é de um operador UNION em sua consulta, algo como isto supondo que seus slugs de categoria são "category-1", "category-1" e "category-3":

SELECT * FROM wp_posts WHERE ID IN (
  SELECT tr.object_id
  FROM wp_terms t 
  INNER JOIN wp_term_taxonomy tt ON t.term_id = tt.term_id
  INNER JOIN wp_term_relationships tr ON tt.term_taxonomy_id = tr.term_taxonomy_id
  WHERE tt.taxonomy='category' AND t.slug='category-1'
  LIMIT 5

  UNION

  SELECT tr.object_id
  FROM wp_terms t 
  INNER JOIN wp_term_taxonomy tt ON t.term_id = tt.term_id
  INNER JOIN wp_term_relationships tr ON tt.term_taxonomy_id = tr.term_taxonomy_id
  WHERE tt.taxonomy='category' AND t.slug='category-2'
  LIMIT 5

  UNION

  SELECT tr.object_id
  FROM wp_terms t 
  INNER JOIN wp_term_taxonomy tt ON t.term_id = tt.term_id
  INNER JOIN wp_term_relationships tr ON tt.term_taxonomy_id = tr.term_taxonomy_id
  WHERE tt.taxonomy='category' AND t.slug='category-3'
  LIMIT 5
)

Você pode usar o SQL UNION com um filtro posts_join

Usando o acima, você pode apenas fazer a chamada diretamente ou você pode usar um gancho de filtro posts_joinlike como segue; note que eu estou usando um PHP heredoc tenha certeza que o SQL; está à esquerda. Observe também que usei um var global para permitir que você defina as categorias fora do gancho listando as slugs de categoria em uma matriz. Você pode colocar este código em um plugin ou no arquivo functions.php do seu tema:

<?php
global $top_5_for_each_category_join;
$top_5_for_each_category_join = array('category-1','category-2','category-3');
add_filter('posts_join','top_5_for_each_category_join',10,2);
function top_5_for_each_category_join($join,$query) {
  global $top_5_for_each_category_join;
  $unioned_selects = array();
  foreach($top_5_for_each_category_join as $category) {
    $unioned_selects[] =<<<SQL
SELECT object_id
FROM wp_terms t
INNER JOIN wp_term_taxonomy tt ON t.term_id = tt.term_id
INNER JOIN wp_term_relationships tr ON tt.term_taxonomy_id = tr.term_taxonomy_id
WHERE tt.taxonomy='category' AND t.slug='{$category}'
LIMIT 5
SQL;
  }
  $unioned_selects = implode("\n\nUNION\n\n",$unioned_selects);
  return $join . " INNER JOIN ($unioned_selects) categories ON wp_posts.ID=categories.object_id " ;
}

Mas pode haver efeitos colaterais

É claro que usar os ganchos de modificação de consulta como posts_join sempre solicita efeitos colaterais, pois eles agem globalmente em consultas e, portanto, você geralmente precisa envolver suas modificações em uma if que só usa quando necessário e quais critérios testar pode ser complicado.

Concentre-se na otimização em vez disso?

No entanto, suponho que sua pergunta esteja relacionada a mais sobre otimização do que sobre poder fazer uma consulta top 5 time 3, certo? Se for esse o caso, então talvez haja outras opções que usam a API do WordPress?

Melhor usar a API de transientes para o cache?

Presumo que suas postagens não mudem com frequência, correto? E se você aceitar a consulta de três (3) ocorrências periodicamente e, em seguida, armazenar os resultados em cache usando a API Transients ? Você terá um código de fácil manutenção e ótimo desempenho; um pouco melhor que a consulta UNION acima porque o WordPress armazenará as listas de posts como um array serializado em um registro da tabela wp_options.

Você pode usar o seguinte exemplo e inserir a raiz do seu site como test.php para testá-lo:

<?php

$timeout = 4; // 4 hours
$categories = array('category-1','category-2','category-3');

include "wp-load.php";
$category_posts = get_transient('top5_posts_per_category');
if (!is_array($category_posts) || count($category_posts)==0) {
  echo "Hello Every {$timeout} hours!";
  $category_posts = array();
  foreach($categories as $category) {
    $posts = get_posts("post_type=post&numberposts=5&taxonomy=category&term={$category}");
    foreach($posts as $post) {
      $category_posts[$post->ID] = $post;
    }
  }
  set_transient('top5_posts_per_category',$category_posts,60*60*$timeout);
}
header('Content-Type:text/plain');
print_r($category_posts);

Resumo

Embora sim, você pode fazer o que solicitou usando uma consulta SQL UNION e o gancho de filtro posts_join que provavelmente é melhor oferecer usando o cache com aAPI de Transientesem vez disso.

Espero que isto ajude?

15
MikeSchinkel

WP_Query() não suporta algo como Primeiro X para cada gato . Em vez de WP_Query() você pode usar get_posts() em cada uma das suas categorias, por assim dizer três vezes:

<?php
$posts      = array():
$categories = array(2,4,8);
foreach($categories as $cat) {
  $posts[] = get_posts('numberposts=5&offset=1&category='.$cat);
}
?>

$posts agora contém os primeiros cinco posts para cada categoria.

1
hakre

Não conheço uma maneira de obter as cinco primeiras postagens para cada uma das categorias em uma única consulta. Se você tiver apenas 45 postagens, bater no banco de dados uma vez e obter seus cinco posts para cada categoria é provavelmente a abordagem mais eficiente. Bater o banco de dados três vezes e combinar os resultados não é uma coisa ruim.

0
Bernard Chen