Verwenden Sie WP_Query, um mehrere Kategorien mit begrenzten Beiträgen pro Kategorie abzufragen?


10

Ich habe 3 Kategorien mit jeweils 15 Beiträgen. Ich möchte EINE Abfrage an die Datenbank senden und nur 5 erste Beiträge für jede Kategorie erstellen. Wie kann ich das tun?

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

Falls dies nicht möglich ist, was ist effizienter, wenn alle Beiträge für die übergeordnete Kategorie abgerufen und durchlaufen werden oder 3 verschiedene Abfragen erstellt werden?


Wie sollen sie bestellt werden?
MikeSchinkel

kürzlich veröffentlicht .. also die neuesten 5 der Kategorie A, die neuesten Sachen der Kategorie B etc ..
Amit

Okay, siehe meine Antwort unten
MikeSchinkel

Antworten:


16

Was Sie wollen, ist möglich, erfordert jedoch, dass Sie sich mit SQL befassen, was ich nach Möglichkeit vermeiden möchte (nicht, weil ich es nicht weiß, ich bin ein fortgeschrittener SQL-Entwickler, sondern weil Sie in WordPress die API verwenden möchten, wann immer dies möglich ist um zukünftige Kompatibilitätsprobleme im Zusammenhang mit zukünftigen möglichen Änderungen der Datenbankstruktur zu minimieren.)

SQL mit einem UNIONOperator ist eine Möglichkeit

So verwenden Sie SQL , was Sie brauchen ist ein UNIONOperator in Ihrer Abfrage, so etwas wie dies sind Ihre Kategorie Schnecken unter der Annahme "category-1", "category-1"und "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
)

Sie können SQL UNION mit einem posts_joinFilter verwenden

Mit den oben genannten Informationen können Sie entweder einfach direkt anrufen oder einen posts_joinFilter-Hook wie folgt verwenden. Hinweis: Ich verwende einen PHP-Heredoc. Stellen Sie daher sicher, dass der HeredocSQL; bündig ist. Beachten Sie auch, dass ich eine globale Variable verwendet habe, damit Sie die Kategorien außerhalb des Hooks definieren können, indem Sie die Kategorie-Slugs in einem Array auflisten. Sie können diesen Code in ein Plugin oder in die functions.phpDatei Ihres Themas einfügen :

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

Aber es kann Nebenwirkungen geben

Natürlich führt die Verwendung der Abfrageänderungs-Hooks wie posts_joinimmer zu Nebenwirkungen, da sie global auf Abfragen wirken. Daher müssen Sie Ihre Änderungen normalerweise in eine einbinden if, die sie nur bei Bedarf verwendet und auf welche Kriterien getestet werden kann.

Stattdessen auf Optimierung setzen?

Ich gehe jedoch davon aus, dass es bei Ihrer Frage mehr um Optimierung geht als darum, eine Top-5-Zeit-3-Abfrage durchführen zu können, oder? Wenn dies der Fall ist, gibt es vielleicht andere Optionen, die die WordPress-API verwenden?

Besser die Transienten-API für das Caching verwenden?

Ich gehe davon aus, dass sich Ihre Beiträge nicht so oft ändern werden, richtig? Was ist, wenn Sie die drei (3) Abfragetreffer regelmäßig akzeptieren und die Ergebnisse dann mithilfe der Transienten-API zwischenspeichern ? Sie erhalten wartbaren Code und eine hervorragende Leistung. Ein gutes Stück besser als die UNIONobige Abfrage, da WordPress die Liste der Beiträge als serialisiertes Array in einem Datensatz der wp_optionsTabelle speichert .

Sie können das folgende Beispiel verwenden und im Stammverzeichnis Ihrer Website test.phpablegen, um dies zu testen:

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

Zusammenfassung

Ja, Sie können zwar das tun, wonach Sie gefragt haben, indem Sie eine SQL- UNIONAbfrage und den posts_joinFilter-Hook verwenden, den Sie wahrscheinlich besser mit Caching mit der Transients-API anbieten .

Hoffe das hilft?


2
Das Zwischenspeichern über Transienten ist eine nette Sache, um die Auswirkungen auf die Site zu verringern.
hakre

1
@ Mike tolle Idee zur Verwendung der Transienten.
Chris_O

@ Mike, wenn ich auf deinem Kontinent leben würde, hätte ich Unterricht bei dir genommen! Danke noch einmal!
Amit

@Amit: LOL! :-)
MikeSchinkel

1
Könnten Sie den Transienten auch auf unbestimmte Zeit speichern und dann auf save_post spülen? Es gibt ein gutes Beispiel (mit edit_term hook) im Codex
helgatheviking

1

WP_Query()unterstützt nicht so etwas wie First X For Each Cat . Stattdessen WP_Query()können Sie get_posts()für jede Ihrer Kategorien sozusagen dreimal verwenden:

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

$posts enthält jetzt die ersten fünf Beiträge für jede Kategorie.


sind das nicht 3 Treffer für die Datenbank? Ich wollte wissen, ob Sie es mit einem Treffer schaffen, weil ich dachte, es ist effizienter.
Amit

Nun, dafür ist die Datenbank da, oder? Fragen Sie die Daten ab. Die Datenbank ist für solche Sachen gemacht. Es ist wahrscheinlich schneller als das Ausführen einer komplizierten SQL-Abfrage, nach der Sie fragen. Hab keine Angst, das Zeug zu benutzen. Wenn es Ihre Website beschädigt, melden Sie es hier.
hakre

mm .. verstanden, ich weiß nicht viel über die Effizienz von PHP / MySQL / DB (würde gerne mehr lesen), war sicher, dass weniger Treffer besser sind und analysiere dann das Ergebnis selbst.
Amit

1
Nun, wahr, im Allgemeinen ist mehr mehr und weniger ist weniger. Aber versuchen Sie es zuerst. Je "schlechter" Ihre Software ist, desto mehr Potenzial hat sie für die Optimierung. Lernen Sie also besser, denn am Ende schreiben Sie schneller bessere Software und schreiben schneller schnellere Software.
hakre

0

Ich kenne keine Möglichkeit, die ersten fünf Beiträge für jede der Kategorien in einer einzigen Abfrage abzurufen. Wenn Sie jemals nur 45 Beiträge haben werden, ist es wahrscheinlich der effizienteste Ansatz, einmal auf die Datenbank zuzugreifen und Ihre fünf Beiträge für jede Kategorie zu erhalten. Es ist jedoch keine schlechte Sache, dreimal auf die Datenbank zuzugreifen und die Ergebnisse zu kombinieren.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.