WP DEVELOP

EP035. “用 pre_get_posts 调整默认查询(而不是重写归档页)”

首页 WordPress 开发课程 CUSTOM POST TYPE 入门 · EP035
约 12 分钟· #EP035#CUSTOM POST TYPE 入门
🔒 登录后可标记已读

上一讲把首页的 Upcoming Events 按 event_date 排序并过滤掉过去的活动,但「View All Events」归档页(archive-event.php)还是照 WordPress 默认查询(按发布日期排序,不过滤过去活动)。第一反应可能是在 archive-event.php 里也写一个全新的 WP_Query,但这一讲的重点是:归档页本来就有 WordPress 自动生成的默认查询,没必要整个推翻重写,直接重写会让分页(pagination)变得麻烦。更好的做法是用 pre_get_posts 这个 hook,在 WordPress 把查询送去数据库「之前」,拿到这个默认查询对象,直接在原有查询上做几个参数微调。首页的 Upcoming Events 因为本来就不是默认查询该管的范围(默认查询只会查首页这一个页面),所以那边继续保留自定义 WP_Query 是对的;但归档页这种「跟当前 URL 强绑定」的场景,调整默认查询才是正确工具。

涉及文件

  • wp-content/themes/fictional-university-theme/functions.php (修改:新增 university_adjust_queries() 函数,挂到 pre_get_posts
  • wp-content/themes/fictional-university-theme/archive-event.php (修改:日期圈从写死的 Mar/25 换成动态取值,跟首页写法一致)
  • wp-content/themes/fictional-university-theme/front-page.php (修改:posts_per_page 从上一讲实验用的 -1 改回 2;排序/过滤参数不变)

代码实现


1. functions.php —— 新增 pre_get_posts 钩子函数

// wp-content/themes/fictional-university-theme/functions.php
// 加在文件最后:

function university_adjust_queries($query) {
  if (!is_admin() AND is_post_type_archive('event') AND $query->is_main_query()) {
    $today = date('Ymd');
    $query->set('meta_key', 'event_date');
    $query->set('orderby', 'meta_value_num');
    $query->set('order', 'ASC');
    $query->set('meta_query', array(
              array(
                'key' => 'event_date',
                'compare' => '>=',
                'value' => $today,
                'type' => 'numeric'
              )
            ));
  }
}

add_action('pre_get_posts', 'university_adjust_queries');

要点:

  • pre_get_posts 这个 hook 会在 WordPress 把查询真正送去数据库之前触发,并把当前的查询对象($query)传给我们的函数,让我们有机会在最后一刻改它的参数。
  • 这个函数一开始(教学演示阶段)如果什么条件判断都不加,会把 posts_per_page 这类调整应用到网站所有查询上——不只是前台 events 归档页,连博客归档、甚至后台文章列表页都会被强制改成一页一条,这是这一讲特地做实验暴露出的问题。
  • 所以最终版加了三层条件把范围收紧:
    1. !is_admin() —— 只在前台生效,不影响后台管理画面;
    2. is_post_type_archive('event') —— 只在 event 的归档页生效,不影响博客归档等其他页面;
    3. $query->is_main_query() —— 只调整「跟当前 URL 绑定的默认主查询」,不会误伤页面里其他手写的自定义 WP_Query
  • 具体调整的四个参数(meta_key/orderby/order/meta_query)跟 EP034 首页那套逻辑完全一样,直接照抄过来改成用 $query->set($key, $value) 的写法逐个设置。

2. archive-event.php —— 日期圈换成动态数据

// wp-content/themes/fictional-university-theme/archive-event.php
// while 循环里的日期圈,从写死的 Mar / 25 换成:

<a class="event-summary__date t-center" href="#">
  <span class="event-summary__month"><?php
    $eventDate = new DateTime(get_field('event_date'));
    echo $eventDate->format('M')
  ?></span>
  <span class="event-summary__day"><?php echo $eventDate->format('d') ?></span>  
</a>

这段代码直接从首页复制过来,跟 EP032/EP034 的写法一致。注意这个循环用的仍是裸的 have_posts()/the_post()(因为走的是默认查询,只是被 pre_get_posts 悄悄调整过参数,模板本身不需要知道这件事)。


3. front-page.php —— posts_per_page 改回 2

// wp-content/themes/fictional-university-theme/front-page.php
// Upcoming Events 的 WP_Query 参数里:

'posts_per_page' => 2, // 从上一讲实验用的 -1 改回 2,恢复首页只显示最近两场活动

首页这个查询的 meta_key/orderby/order/meta_query 部分维持 EP034 的写法不变,只有 posts_per_page 改回 2

Hook / Function 速查

名称类型用途
pre_get_postsWP hook在 WordPress 执行查询前触发,可以拿到查询对象并修改其参数
$query->set($key, $value)WP_Query 方法pre_get_posts 里用来设置/覆盖某个查询参数
is_admin()WP 内建 function判断当前是否在后台管理画面
is_post_type_archive($post_type)WP 内建 function判断当前是否在指定文章类型的归档页
$query->is_main_query()WP_Query 方法判断传入的查询是否是跟当前 URL 绑定的主查询(而非页面里额外手写的自定义查询)

常见坑

  • pre_get_posts 挂上去后如果不加任何条件判断,会把参数调整应用到网站所有查询:不仅前台博客归档会被误改,连后台的文章列表、事件列表也会被强制改成一页一条——这是这一讲特意演示的坑,用来说明为什么必须加条件收紧范围。
  • 只用 is_admin() 判断不够精确,还需要加 is_post_type_archive('event'),否则前台博客归档也会被一起改掉。
  • 再加上 $query->is_main_query() 作为第三层保险,避免以后页面上出现别的自定义 WP_Query 时被误伤。

延伸 / 后续讲座会用到

下一讲 EP036 会做一个新的「Past Events」页面模板,需要用到自定义 WP_Query(因为这不是默认 URL 查询能覆盖的场景),并且要额外处理自定义查询下分页失效的问题。

Sources

Udemy:

  • Become a WordPress Developer: Unlocking Power With Code — Section 7, EP035