Utilisation de WP_Query pour interroger plusieurs catégories avec des publications limitées par catégorie?


10

J'ai 3 catégories avec chacune 15 messages, je veux faire UNE requête à la base de données en apportant seulement 5 premiers messages pour chaque catégorie, comment puis-je le faire?

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

Dans le cas où ce n'est pas possible, qu'est-ce qui est plus efficace, obtenir tous les messages pour la catégorie parent et les parcourir ou créer 3 requêtes différentes?


Comment voulez-vous qu'ils soient commandés?
MikeSchinkel

récemment publié .. donc les 5 plus récents de la catégorie A, les trucs les plus récents de la catégorie B etc.
Amit

D'accord, voir ma réponse ci
MikeSchinkel

Réponses:


16

Ce que vous voulez est possible mais vous obligera à vous plonger dans SQL que j'aime éviter autant que possible (pas parce que je ne le sais pas, je suis un développeur SQL avancé, mais parce que dans WordPress, vous voulez utiliser l'API autant que possible pour minimiser les futurs problèmes de compatibilité liés aux futurs changements potentiels de la structure de la base de données.)

SQL avec un UNIONopérateur est une possibilité

Pour utiliser SQL, vous avez besoin d'un UNIONopérateur dans votre requête, quelque chose comme ceci en supposant que vos slugs de catégorie sont "category-1", "category-1"et "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
)

Vous pouvez utiliser SQL UNION avec un posts_joinfiltre

En utilisant ce qui précède, vous pouvez simplement passer l'appel directement ou vous pouvez utiliser un posts_joincrochet de filtre comme suit; note J'utilise un hérédoc PHP, alors assurez-vous que le SQL;flush est à gauche. Notez également que j'ai utilisé une var globale pour vous permettre de définir les catégories en dehors du hook en listant les slugs de catégorie dans un tableau. Vous pouvez mettre ce code dans un plugin ou dans le functions.phpfichier de votre thème :

<?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 " ;
}

Mais il peut y avoir des effets secondaires

Bien sûr, l'utilisation des crochets de modification de requête, comme posts_jointoujours, provoque des effets secondaires dans la mesure où ils agissent globalement sur les requêtes et donc vous devez généralement envelopper vos modifications dans un ifqui ne l'utilise qu'en cas de besoin et quels critères pour tester peuvent être délicats.

Concentrez-vous plutôt sur l'optimisation?

Cependant, je suppose que votre question concerne plus l'optimisation que la possibilité de faire une requête top 5 en temps 3, non? Si tel est le cas, il existe peut-être d'autres options qui utilisent l'API WordPress?

Mieux utiliser l'API transitoires pour la mise en cache?

Je suppose que vos messages ne changeront pas souvent, n'est-ce pas? Que faire si vous acceptez périodiquement les trois (3) requêtes, puis mettez en cache les résultats à l'aide de l' API Transitoires ? Vous obtiendrez du code maintenable et de grandes performances; un peu mieux que la UNIONrequête ci-dessus car WordPress stockera les listes de publications sous forme de tableau sérialisé dans un enregistrement de la wp_optionstable.

Vous pouvez prendre l'exemple suivant et passer à la racine de votre site Web test.phppour le tester:

<?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);

Résumé

Bien que oui, vous pouvez faire ce que vous avez demandé en utilisant une UNIONrequête SQL et le posts_joincrochet de filtre que vous proposez probablement mieux en utilisant la mise en cache avec l' API Transitoires à la place.

J'espère que cela t'aides?


2
La mise en cache via des transitoires est une bonne chose pour réduire l'impact sur le site.
hakre

1
@Mike excellente idée sur l'utilisation des transitoires.
Chris_O

@Mike, si je vivais sur votre continent, j'aurais pris des cours avec vous! Merci encore!
Amit

@Amit: LOL! :-)
MikeSchinkel

1
pourriez-vous également enregistrer le transitoire indéfiniment, puis le vider sur save_post? il y a un bon exemple (en utilisant le crochet edit_term) dans le codex
helgatheviking

1

WP_Query()ne prend pas en charge quelque chose comme First X For Each Cat . Au lieu de WP_Query()vous pouvez utiliser get_posts()sur chacune de vos catégories, pour ainsi dire trois fois:

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

$posts contient désormais les cinq premiers messages pour chaque catégorie.


n'est-ce pas 3 coups sûrs pour la base de données? Je voulais savoir si vous pouvez le faire en 1 coup car je pensais que c'était plus efficace.
Amit

c'est à ça que sert le db, non? interroger les données de celui-ci. le db est fait pour de telles choses. il est probablement plus rapide que d'exécuter une requête SQL complexe que vous demandez. n'ayez pas peur d'utiliser les trucs. s'il casse votre site, signalez-le ici.
hakre

mm .. j'ai compris, je ne sais pas grand chose sur l'efficacité de php / mysql / db (j'aimerais en savoir plus), j'étais sûr que moins de hits est meilleur et ensuite analyser le résultat moi-même.
Amit

1
eh bien, généralement, plus c'est plus et moins c'est moins. Mais essayez d'abord de tester. Plus votre logiciel est "mauvais", plus il a de potentiel d'optimisation. Mieux vaut donc apprendre en faisant, car au final, vous écrirez un meilleur logiciel plus rapidement et une meilleure écriture plus rapide.
hakre

0

Je ne connais pas de moyen d'obtenir les cinq premiers messages pour chacune des catégories dans une seule requête. Si vous n'avez jamais que 45 messages, alors frapper la base de données une fois et obtenir vos cinq messages pour chaque catégorie est probablement l'approche la plus efficace. Frapper la base de données trois fois et combiner les résultats n'est pas une mauvaise chose.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.