Використання 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.