Использование WP_Query для запроса нескольких категорий с ограниченным количеством сообщений в категории?


10

У меня есть 3 категории с каждыми 15 сообщениями, я хочу сделать ОДИН запрос к БД, принося только 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;он слева. Также обратите внимание, что я использовал глобальную переменную, чтобы позволить вам определять категории вне ловушки, перечисляя слагов категорий в массиве. Вы можете поместить этот код в плагин или в 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 Transients ? Вы получите поддерживаемый код и отличную производительность; это немного лучше, чем 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);

Резюме

Хотя да, вы можете делать то, о чем просили, используя SQL- UNIONзапрос и posts_joinфильтр-хук, но вам, вероятно, лучше предложить использовать кеширование с помощью Transient API .

Надеюсь это поможет?


2
Кэширование через переходные процессы - это хорошая вещь, чтобы уменьшить влияние на сайт.
2010 года

1
@ Отличная идея по использованию переходных процессов.
Chris_O

@ Майк, если бы я жил на твоем континенте, я бы пошел с тобой на занятия! Спасибо еще раз!
Амит

@Amit: LOL! :-)
MikeSchinkel

1
Не могли бы вы также сохранить переходный процесс на неопределенный срок, а затем сбросить его на save_post? есть хороший пример (с использованием хита edit_term) в кодексе
helgatheviking

1

WP_Query()не поддерживает что-то вроде First X For Each 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 попадания в БД? Я хотел знать, можете ли вы сделать это за 1 удар, потому что я думал, что это более эффективно.
Амит

ну вот для чего нужен БД, верно? запросить данные из него. БД сделан для таких вещей. это, вероятно, быстрее, чем выполнение сложного SQL-запроса, который вы запрашиваете. не бойся использовать вещи. если это сломает ваш сайт, сообщите об этом здесь.
Хакре

мм .. понял, я не знаю много об эффективности php / mysql / db (хотел бы прочитать больше), был уверен, что меньше хитов лучше, а потом сам разбираю результат.
Амит

1
ну, правда, как правило, больше больше, а меньше меньше. Но попробуйте сначала проверить. Чем «хуже» ваше программное обеспечение, тем больше у него потенциала для оптимизации. Так что лучше учитесь на практике, потому что в конце вы будете писать лучшее программное обеспечение быстрее, а лучшее - быстрее.
Хакре

0

Я не знаю, как получить первые пять сообщений для каждой категории в одном запросе. Если у вас когда-нибудь будет только 45 сообщений, то, пожалуй, самый эффективный подход - зайти в базу данных и получить пять сообщений для каждой категории. Попадание в базу данных три раза и объединение результатов не так уж и плохо.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.