Використання WP_Query для запиту кількох категорій з обмеженими повідомленнями на категорію?


10

У мене є 3 категорії з кожними 15 повідомленнями, я хочу зробити ОДИН запит до db, приносячи лише 5 перших публікацій для кожної категорії, як це зробити?

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

Якщо це неможливо, що ефективніше, отримуючи всі повідомлення для батьківської категорії та перебираючи їх або створюючи 3 різних запиту?


Як ви хочете, щоб вони були замовлені?
MikeSchinkel

нещодавно опубліковано .. тому найсвіжіші 5 категорії A, найсвіжіші матеріали категорії B тощо.
Amit

Гаразд, дивіться мою відповідь нижче
MikeSchinkel

Відповіді:


16

Те, що ви хочете, можливо, але зажадає від вас заглибитись у SQL, який мені подобається уникати, коли це можливо (не тому, що я цього не знаю, я заздалегідь розробник SQL, а тому, що в WordPress ви хочете використовувати API, коли це можливо щоб мінімізувати майбутні проблеми сумісності, пов’язані з майбутніми потенційними змінами структури бази даних.)

SQL з UNIONоператором - це можливість

Щоб використовувати SQL, UNIONу вашому запиті потрібен оператор, приблизно на зразок цього, якщо ви вважаєте, що ваші категорії категорій є "category-1", "category-1"і "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
)

Ви можете використовувати SQL UNION з posts_joinфільтром

Використовуючи вищесказане, ви можете або просто здійснити дзвінок безпосередньо, або ви можете використовувати posts_joinгачок фільтра, як описано нижче. Зауважте, що я використовую PHP heredoc, тому будьте впевнені, що залишокSQL; залишився. Також зауважте, що я використовував глобальний var, щоб дозволити вам визначати категорії за межами гака, перераховуючи категорії молюсків у масиві. Ви можете помістити цей код у плагін або у functions.phpфайл вашої теми :

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

Але можливі побічні ефекти

Звичайно, використання гачків для модифікації запитів, як posts_joinзавжди, викликає побічні ефекти, оскільки вони діють у глобальному масштабі на запити, і, таким чином, вам зазвичай потрібно обернути свої модифікації в тому, ifщо використовує його лише тоді, коли це потрібно, і які критерії для перевірки можуть бути складними.

Натомість зосередитись на оптимізації?

Однак, я припускаю, що ваше питання стосується скоріше оптимізації, ніж того, щоб мати змогу виконати запит перших 5 разів 3, правда? Якщо це так, то, можливо, є інші варіанти, які використовують API WordPress?

Краще використовувати API перехідних процесів для кешування?

Я припускаю, що ваші пости не змінюватимуться так часто, правда? Що робити, якщо ви періодично приймаєте звернення до трьох (3) запитів, а потім кешуєте результати за допомогою API перехідних процесів ? Ви отримаєте бездоганний код та велику продуктивність; трохи краще, ніж UNIONзапит вище, тому що WordPress буде зберігати списки публікацій у вигляді серіалізованого масиву в одному записі wp_optionsтаблиці.

Ви можете взяти наступний приклад і запустити корінь веб-сайту, test.phpщоб перевірити це:

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

Підсумок

Хоча так, ви можете виконувати те, що ви просили, використовуючи UNIONзапит SQL та posts_joinгачок фільтра, ви, мабуть, краще запропонувати використовувати кешування з API Transients .

Сподіваюся, це допомагає?


2
Кешування за допомогою перехідних процесів - це приємна річ, щоб зменшити вплив на сайт.
хакре

1
@Mike чудова ідея використання перехідних процесів.
Chris_O

@Mike, якби я жив на твоєму континенті, я б брав з тобою заняття! знову дякую!
Аміт

@Amit: LOL! :-)
MikeSchinkel

1
Ви можете також зберегти перехідний на невизначений термін, а потім передати його на save_post? є хороший приклад (використовуючи edit_term гачок) у кодексі
helgatheviking

1

WP_Query()не підтримує щось на зразок First X For Every Cat . Замість цього WP_Query()ви можете використовувати get_posts()для кожної своєї категорії, так би мовити три рази:

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

$posts тепер містить перші п’ять публікацій для кожної категорії.


це не 3 хіти до db? Мені хотілося знати, чи можна це зробити за 1 удар, тому що я думав, що це більш ефективно.
Аміт

ну ось для чого потрібний db, правда? запитувати дані з нього. db зроблений для таких речей. це, ймовірно, швидше, ніж запуск складного запиту sql, про який ви просите. не бійтеся використовувати речі. якщо він порушує ваш сайт, повідомте про це тут.
хакре

мм.
Аміт

1
ну правда, загалом більше більше, а менше менше. Але спробуйте спочатку. Чим "поганіше" ваше програмне забезпечення, тим більше потенціалу воно має для оптимізації. Тож краще вчись робити, адже врешті-решт ти будеш писати краще програмне забезпечення швидше та краще писати швидше програмне забезпечення.
хакре

0

Я не знаю способу отримати перші п’ять публікацій для кожної категорії за один запит. Якщо ви коли-небудь матимете лише 45 публікацій, то натискання на базу даних один раз і отримання ваших п’яти повідомлень для кожної категорії, мабуть, є найбільш ефективним підходом. Потрапляння в базу даних три рази і комбінування результатів не є поганою справою.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.