问题描述

我有 3 个类别,每 15 个帖子,我想做一个查询数据库,每个类别只有 5 个第一个帖子,我该怎么办?

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

如果不可能,什么是更有效率,获取父类别的所有帖子并循环遍历或创建 3 个不同的查询?

最佳解决方案

你想要的是可能的,但是需要你深入研究我喜欢避免的 SQL(不是因为我不知道,我是一个先进的 SQL 开发人员,而是因为在 WordPress 中,你可以尽可能地使用 API 以尽量减少与未来潜在数据库结构变化相关的未来兼容性问题。)

具有 UNION 运算符的 SQL 是可能性

要使用 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; 是齐平的。另请注意,我使用全局变量,允许您通过列出数组中的类别 lug 子来定义钩子外的类别。您可以将此代码放入插件或主题的 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("nnUNIONnn",$unioned_selects);
  return $join . " INNER JOIN ($unioned_selects) categories ON wp_posts.ID=categories.object_id " ;
}

但是可以是 Side-effects

当然,使用像 posts_join 这样的查询修改钩子总是邀请 side-effects,因为它们在查询上全局地执行操作,因此通常需要在 if 中进行修改,if 只在需要时使用它,并且要测试的标准可能很棘手。

专注于优化?

然而,我认为你的问题更关心优化是关于能够做一个前 5 名 3 查询,对吗?如果是这种情况,那么也许还有其他使用 WordPress API 的选项?

更好地使用瞬态 API 进行缓存?

我认为你的帖子不会经常改变,正确吗?如果您定期接受三 (3) 个查询,然后使用 Transients 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);

总结

虽然是的,但您可以使用 SQL UNION 查询和 posts_join 筛选器钩子来进行所需的操作,而不是使用 Transient API 进行缓存。

希望这可以帮助?

参考文献

注:本文内容整合自 Google/Baidu/Bing 辅助翻译的英文资料结果。如果您对结果不满意,可以加入我们改善翻译效果:薇晓朵技术论坛。