EP161. “用 the_content 过滤器与 meta_query 查反向关系”
🔒 登录后可标记已读Featured Professor 插件的收尾一讲:在 Professor 详情页正文末尾自动追加一段「本教授被以下文章提到」的列表,把 EP160 存好的 featuredprofessor 元数据反向查询出来用上,让这段关系真正变成「双向可查」。做法是用 the_content 过滤器钩子,只在「Professor 详情页 + 在循环里 + 是主查询」三个条件都满足时才往正文末尾追加内容;具体查询用 WP_Query 的 meta_query 参数——按元数据的 key/value 反查出所有存过「这位 Professor 的 ID」的文章。这也是这门课 JS 章节里 Featured Professor 这个插件的最后一讲,下一讲开始讲 JavaScript 端的多语言/翻译。
涉及文件
wp-content/plugins/featured-professor/featured-professor.php(修改)wp-content/plugins/featured-professor/inc/relatedPostsHTML.php(新建)
代码实现
featured-professor.php:新增 the_content 过滤器,条件判断后追加关联文章列表:
require_once plugin_dir_path(__FILE__) . 'inc/generateProfessorHTML.php';
require_once plugin_dir_path(__FILE__) . 'inc/relatedPostsHTML.php';
class FeaturedProfessor {
function __construct() {
add_action('init', [$this, 'onInit']);
add_action('rest_api_init', [$this, 'profHTML']);
add_filter('the_content', [$this, 'addRelatedPosts']);
}
function addRelatedPosts($content) {
if (is_singular('professor') && in_the_loop() && is_main_query()) {
return $content . relatedPostsHTML(get_the_id());
}
return $content;
}
// ...profHTML() / getProfHTML() / onInit() / renderCallback() 不变,见 EP157-160...
}
inc/relatedPostsHTML.php(新建):用 meta_query 反查所有提到这位 Professor 的文章:
<?php
function relatedPostsHTML($id) {
$postsAboutThisProf = new WP_Query(array(
'posts_per_page' => -1,
'post_type' => 'post',
'meta_query' => array(
array(
'key' => 'featuredprofessor',
'compare' => '=',
'value' => $id
)
)
));
ob_start();
if ($postsAboutThisProf->found_posts) { ?>
<p><?php the_title(); ?> is mentioned in the following posts:</p>
<ul>
<?php
while($postsAboutThisProf->have_posts()) {
$postsAboutThisProf->the_post(); ?>
<li><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></li>
<?php }
?>
</ul>
<?php }
wp_reset_postdata();
return ob_get_clean();
}
关键改动点:
add_filter('the_content', ...):the_content是 WordPress 里输出文章正文时会经过的一个标准过滤器钩子,回调函数接收「原始正文」当参数,return的值会替换成最终真正显示的正文——这是「往正文末尾追加内容,又不用直接改模板文件」的标准做法- 三重条件缺一不可:
is_singular('professor'):当前是不是 Professor 这个文章类型的单篇详情页(避免在文章列表页、其他文章类型详情页也误触发)in_the_loop():当前是不是正处于 WordPress 的主循环内部——因为the_content过滤器理论上任何用到the_content()/get_the_content()的地方都可能触发,包括一些插件在后台或者其他自定义查询里也调用这个函数,加这个判断确保只在真正渲染主循环的正文时生效is_main_query():当前是不是页面的主查询(而不是页面里某个自定义的次要查询,比如侧边栏的「相关文章」小工具也可能内部调用了the_content)——三个条件一起用是这门课作者一贯的保守写法,避免函数在意料之外的场景被误触发
$content . relatedPostsHTML(get_the_id()):用字符串拼接(.)把原始正文和新生成的关联文章列表 HTML 接在一起,get_the_id()拿到当前 Professor 文章的 ID,传给新写的relatedPostsHTML()函数- 同样把大段查询 + HTML 拆到独立文件:延续 EP157 的组织习惯,新建
inc/relatedPostsHTML.php,主插件文件顶部再加一行require_once引入它 WP_Query的meta_query参数:用来「按元数据筛选文章」——最外层是一个数组,里面可以放多个查询条件数组(这里只用了一个),每个条件数组里:'key' => 'featuredprofessor':要匹配的元数据名字,必须跟 EP160register_meta()注册的名字完全一致'compare' => '=':比较方式,这里要求精确匹配'value' => $id':要匹配的值,也就是当前这位 Professor 的文章 ID- 效果等同于「查询所有
post类型的文章里,凡是存过一条meta_key = featuredprofessor且meta_value等于这位 Professor ID 的文章」
'posts_per_page' => -1:一次拿全部符合条件的结果,不分页if ($postsAboutThisProf->found_posts):found_posts是WP_Query对象上的一个属性,记录查询结果的总条数——只有真的查到至少一篇相关文章时才输出这段「被提到」的介绍文字和列表,避免完全没有关联文章时也显示一段空的「is mentioned in the following posts:」<?php the_title(); ?> is mentioned in the following posts::这里的the_title()用的是外层还没进入while循环时的全局上下文——此时全局当前文章依然是这个 Professor 详情页本身(因为还没执行$postsAboutThisProf->the_post()),所以这里输出的是 Professor 自己的名字,不是某篇关联文章的标题while ($postsAboutThisProf->have_posts()) { $postsAboutThisProf->the_post(); ... }:标准的自定义查询循环写法(跟 EP157 的generateProfessorHTML()一致),每一轮循环把全局「当前文章」切换成这次遍历到的这篇关联文章,这样循环体内的the_permalink()/the_title()才会输出正确的那篇文章的信息wp_reset_postdata():循环结束后照例要重置全局文章状态,避免影响页面剩余部分(这里是 Professor 详情页自身接下来的渲染)
Hook / Function 速查
| 名称 | 类型 | 用途 |
|---|---|---|
the_content(过滤器) | WP hook | 文章正文输出前的标准过滤器,用来动态追加/修改正文内容 |
is_singular($post_type) | WP 内建 function | 判断当前是否处于指定文章类型的单篇详情页 |
in_the_loop() | WP 内建 function | 判断当前是否处于 WordPress 主循环内部 |
is_main_query() | WP 内建 function | 判断当前查询是否为页面的主查询(区分自定义次要查询) |
meta_query(WP_Query 参数) | WP 查询参数 | 按文章元数据的 key/value 筛选文章 |
$query->found_posts | WP_Query 对象属性 | 查询结果的总条数 |
get_the_id() | WP 内建 function | 获取当前全局文章的 ID |
常见坑
- 只判断
is_singular('professor')就直接追加内容,不检查in_the_loop()/is_main_query()——万一页面其他地方(侧边栏小工具、其他自定义查询)也调用了正文相关的函数,可能在意料之外的地方重复触发这段追加逻辑 meta_query里的key拼错或者跟register_meta()注册的名字对不上——查询会返回空结果,即使数据库里其实已经正确存了关联数据- 忘记判断
found_posts是否大于零——完全没有关联文章的 Professor 详情页也会显示一句不知所云的「被以下文章提到:」但后面是空列表 - 自定义查询循环结束后忘记
wp_reset_postdata()——影响 Professor 详情页自身接下来的渲染,导致标题、内容等显示错乱
[截图:Professor 详情页正文末尾自动追加的"is mentioned in the following posts"关联文章列表]
Sources
Udemy:
- Become a WordPress Developer: Unlocking Power With Code — Section 26, EP161