Używasz WP_Query do zapytania o wiele kategorii z ograniczoną liczbą postów według kategorii?

10

Mam 3 kategorie z każdym 15 postów, chcę wykonać JEDNE zapytanie do bazy danych, przynosząc tylko 5 pierwszych postów dla każdej kategorii, jak mogę to zrobić?

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

W przypadku, gdy nie jest to możliwe, co jest bardziej wydajne, pobieranie wszystkich postów dla kategorii nadrzędnej i przechodzenie między nimi lub tworzenie 3 różnych zapytań?

Amit
źródło
Jak chcesz je zamówić?
MikeSchinkel,
ostatnio opublikowane .. więc ostatnie 5 kategorii A, najnowsze rzeczy kategorii B itp.
Amit
Dobra, zobacz moją odpowiedź poniżej
MikeSchinkel,

Odpowiedzi:

16

To, czego chcesz, jest możliwe, ale wymaga zagłębiania się w SQL, którego lubię unikać, gdy tylko jest to możliwe (nie dlatego, że nie wiem, jestem zaawansowanym programistą SQL, ale dlatego, że w WordPress chcesz używać interfejsu API, gdy tylko jest to możliwe aby zminimalizować przyszłe problemy ze zgodnością związane z przyszłymi potencjalnymi zmianami struktury bazy danych).

SQL z UNIONoperatorem to możliwość

Aby użyć SQL czego potrzebujesz to UNIONoperator w zapytaniu, coś takiego zakładając kategorię ślimaki są "category-1", "category-1"i "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
)

Możesz użyć SQL UNION z posts_joinfiltrem

Korzystając z powyższego, możesz po prostu bezpośrednio nawiązać połączenie lub użyć posts_joinhaka filtrującego w następujący sposób; Uwaga: Używam heredoc PHP, więc upewnij się, że SQL;pozostało kolor. Zauważ też, że użyłem globalnego var, aby umożliwić ci zdefiniowanie kategorii poza hakiem, umieszczając informacje o kategorii w tablicy. Możesz umieścić ten kod we wtyczce lub w functions.phppliku motywu :

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

Ale mogą wystąpić skutki uboczne

Oczywiście użycie haków do modyfikacji zapytań, jak posts_joinzawsze, wywołuje skutki uboczne, ponieważ działają one globalnie na zapytania, a zatem zwykle trzeba zawinąć modyfikacje w taki, ifktóry używa go tylko wtedy, gdy jest to potrzebne, a kryteria, które należy przetestować, mogą być trudne.

Zamiast tego skupić się na optymalizacji?

Zakładam jednak, że twoje pytanie dotyczy bardziej optymalizacji niż możliwości wykonania zapytania z 5 najlepszych czasów 3, prawda? Jeśli tak jest, to może istnieją inne opcje korzystające z interfejsu API WordPress?

Lepiej używać przejściowego interfejsu API do buforowania?

Zakładam, że twoje posty nie będą się tak często zmieniać, prawda? Co się stanie, jeśli zaakceptujesz trzy (3) trafienia kwerendy okresowo, a następnie buforujesz wyniki przy użyciu interfejsu Transients API ? Otrzymasz łatwy do utrzymania kod i doskonałą wydajność; znacznie lepiej niż UNIONpowyższe zapytanie, ponieważ WordPress przechowuje listy postów jako szeregową tablicę w jednym rekordzie wp_optionstabeli.

Możesz wziąć następujący przykład i wpaść do katalogu głównego swojej witryny, test.phpaby to przetestować:

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

Podsumowanie

Chociaż tak, możesz zrobić to, o co prosiłeś, używając UNIONzapytania SQL i posts_joinhaka filtru, ale prawdopodobnie lepiej zaoferować użycie buforowania z Transient API .

Mam nadzieję że to pomoże?

MikeSchinkel
źródło
2
Buforowanie za pomocą transjentów to dobra rzecz, aby zmniejszyć wpływ na witrynę.
hakre
1
@Mike świetny pomysł na korzystanie z Transjentów.
Chris_O,
@Mike, gdybym mieszkał na twoim kontynencie, wziąłbym z tobą zajęcia! dzięki jeszcze raz!
Amit
@Amit: LOL! :-)
MikeSchinkel
1
czy możesz również zapisać transjent na czas nieokreślony, a następnie przepłukać go w save_post? jest dobry przykład (użycie haka edit_term) w kodeksie
helgatheviking
1

WP_Query()nie obsługuje czegoś takiego jak First X For Each Cat . Zamiast tego WP_Query()możesz użyć get_posts()każdej ze swoich kategorii, powiedzmy trzy razy:

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

$posts teraz zawiera pierwsze pięć postów dla każdej kategorii.

hakre
źródło
czy to nie 3 trafień do bazy danych? Chciałem wiedzieć, czy możesz to zrobić za jednym trafieniem, ponieważ uważałem, że jest bardziej wydajny.
Amit,
cóż, po to jest db, prawda? odpytuj z niego dane. db jest stworzony dla takich rzeczy. prawdopodobnie jest szybszy niż uruchomienie skomplikowanego zapytania SQL, o które prosisz. nie bój się użyć tego. jeśli zepsuje twoją witrynę, zgłoś to tutaj.
hakre
mm .. rozumiem, nie wiem wiele o wydajności php / mysql / db (chciałbym przeczytać więcej), byłem pewien, że mniej trafień jest lepszy, a następnie sam parsuję wynik.
Amit,
1
cóż, prawda, ogólnie więcej znaczy więcej, a mniej znaczy mniej. Ale spróbuj najpierw. „Im gorsze” jest twoje oprogramowanie, tym większy ma potencjał do optymalizacji. Lepiej więc uczyć się, robiąc, bo w końcu szybciej napiszesz lepsze oprogramowanie, a lepiej szybciej.
hakre
0

Nie znam sposobu na uzyskanie pierwszych pięciu postów dla każdej kategorii w jednym zapytaniu. Jeśli kiedykolwiek będziesz mieć tylko 45 postów, to trafienie do bazy danych raz i uzyskanie pięciu postów dla każdej kategorii jest prawdopodobnie najbardziej wydajnym podejściem. Trzykrotne trafienie do bazy danych i połączenie wyników nie jest złą rzeczą.

Bernard Chen
źródło