EP036. “新建 Past Events 页面模板,并修复自定义查询下的分页”
🔒 登录后可标记已读首页只显示未来的活动,归档页也过滤掉了过去的活动,但如果有访客想回顾学校办过哪些活动呢?这一讲新建一个「Past Events」页面,配上专属模板 page-past-events.php,用自定义 WP_Query 查出 event_date 小于今天的活动(按日期升序排列)。这一讲真正的难点不是查询逻辑本身(跟前面几讲的 meta_query 套路一样),而是自定义查询下分页默认是坏的:paginate_links() 认不出自定义查询有几页,自定义查询本身也不会跟着 URL 里的页码参数走,需要手动把这两件事接起来。
涉及文件
wp-content/themes/fictional-university-theme/page-past-events.php(新建,页面模板,对应 Pages 里创建的 slug 为past-events的页面)wp-content/themes/fictional-university-theme/archive-event.php(修改:底部加一条「查看过去活动」的链接)wp-content/themes/fictional-university-theme/header.php(修改:Events 导航高亮条件里加上 Past Events 页面)
代码实现
1. 先在后台建一个空白页面
Pages → Add New,标题填 Past Events,正文留空,发布。WordPress 会生成 slug past-events(能在编辑页的 Permalink 里看到)。
2. page-past-events.php(新建)
WordPress 会按 page-{slug}.php 的命名规则自动匹配页面专属模板,所以文件名必须精确对应 past-events 这个 slug。内容以 archive-event.php 为起点改,标题/副标题换成 Past Events 专属文案,并且不再用默认查询(因为这个 URL 对应的默认查询只会查到「Past Events」这一个空白页面本身),改成自己的 WP_Query:
// wp-content/themes/fictional-university-theme/page-past-events.php
<?php
get_header(); ?>
<div class="page-banner">
<div class="page-banner__bg-image" style="background-image: url(<?php echo get_theme_file_uri('/images/ocean.jpg') ?>);"></div>
<div class="page-banner__content container container--narrow">
<h1 class="page-banner__title">Past Events</h1>
<div class="page-banner__intro">
<p>A recap of our past events.</p>
</div>
</div>
</div>
<div class="container container--narrow page-section">
<?php
$today = date('Ymd');
$pastEvents = new WP_Query(array(
'paged' => get_query_var('paged', 1),
'post_type' => 'event',
'meta_key' => 'event_date',
'orderby' => 'meta_value_num',
'order' => 'ASC',
'meta_query' => array(
array(
'key' => 'event_date',
'compare' => '<',
'value' => $today,
'type' => 'numeric'
)
)
));
while($pastEvents->have_posts()) {
$pastEvents->the_post(); ?>
<div class="event-summary">
<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>
<div class="event-summary__content">
<h5 class="event-summary__title headline headline--tiny"><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h5>
<p><?php echo wp_trim_words(get_the_content(), 18); ?> <a href="<?php the_permalink(); ?>" class="nu gray">Learn more</a></p>
</div>
</div>
<?php }
echo paginate_links(array(
'total' => $pastEvents->max_num_pages
));
?>
</div>
<?php get_footer();
?>
要点(分页部分是本讲重点):
- 自定义查询里加了两个关键参数解决分页:
'paged' => get_query_var('paged', 1)—— 告诉这个自定义查询「现在应该显示第几页」。get_query_var('paged', 1)会去读 URL 里当前的页码(比如/past-events/page/2/里的2),如果读不到(比如在第一页,URL 不会带/page/1/)就用第二个参数1作为默认值。- 不加这一行的话,无论点「下一页」链接跳到
/page/2/,查询结果永远是第一页的内容,因为自定义查询默认不会理会 URL 上的页码。
echo paginate_links(array('total' => $pastEvents->max_num_pages))——paginate_links()默认只认识 WordPress 自己的默认查询,不知道自定义查询一共有几页,所以要手动把$pastEvents->max_num_pages(这个自定义查询对象自己算出来的总页数)喂给它的total参数,分页链接才会正确出现。- 排序/过滤逻辑跟 EP034/EP035 一样,只是
compare从>=换成了<(只要过去的活动),order仍是ASC(离今天最近的过去活动排最前面)。 - 开发过程中为了测试分页,临时把
posts_per_page设成1强制出现分页链接,测完之后这个参数被删掉了,所以最终代码里没有posts_per_page(用 WordPress 默认的每页 10 条)。
[截图:前台 /past-events 页面,列出已过去活动卡片及底部分页链接的画面]
3. archive-event.php —— 底部加过去活动的链接
// wp-content/themes/fictional-university-theme/archive-event.php
// 加在 echo paginate_links(); 之后:
<hr class="section-break">
<p>Looking for a recap of past events? <a href="<?php echo site_url('/past-events') ?>">Check out our past events archive</a>.</p>
用 site_url('/past-events') 而不是写死完整域名,是为了兼容网站部署在子目录(比如本地用 MAMP/XAMPP 时)的情况,这个函数会根据当前站点设置自动拼出正确的绝对 URL。
4. header.php —— Events 导航在 Past Events 页面也高亮
// wp-content/themes/fictional-university-theme/header.php
// Events 这个 <li> 的高亮判断,从只判断 get_post_type() == 'event',加上 OR 判断:
<li <?php if (get_post_type() == 'event' OR is_page('past-events')) echo 'class="current-menu-item"'; ?>><a href="<?php echo get_post_type_archive_link('event'); ?>">Events</a></li>
因为 Past Events 是一个普通 Page(不是 event 文章类型本身),get_post_type() == 'event' 判断不到,所以要额外用 is_page('past-events') 补上这个条件。
Hook / Function 速查
| 名称 | 类型 | 用途 |
|---|---|---|
page-{slug}.php | 主题模板命名约定 | WordPress 自动匹配某个具体页面(按 slug)专属的模板文件,本讲对应 page-past-events.php |
get_query_var($var, $default) | WP 内建 function | 读取当前 URL 解析出的查询变量,本讲用来读页码 paged,读不到时给默认值 |
$query->max_num_pages | WP_Query 属性 | 该自定义查询根据总结果数和每页条数算出的总页数 |
paginate_links($args) | WP 内建 function(返回值,需 echo) | 生成分页链接,默认只认默认查询,配合自定义查询时要手动传 total 参数 |
site_url($path) | WP 内建 function(返回值) | 根据当前站点设置生成绝对 URL,比写死域名更安全 |
is_page($slug) | WP 内建 function | 判断当前是否是指定 slug 的页面 |
常见坑
- 自定义
WP_Query默认不会理会 URL 里的分页页码,就算点了「下一页」链接、地址栏变成/page/2/,查询结果还是第一页的内容——必须手动加paged => get_query_var('paged', 1)参数才会跟着 URL 走。 paginate_links()默认只知道 WordPress 自己那个跟 URL 绑定的默认查询有几页,自定义查询要手动把$query->max_num_pages传给它的total参数,不然压根不会显示分页链接。- 页面模板文件名必须严格匹配
page-{slug}.php,这里的 slug 要跟后台 Pages 里创建的那个页面的永久链接 slug(past-events)完全一致,大小写和连字符都不能错,否则不会被自动套用。 - 开发时为了测试分页临时加的
posts_per_page => 1,测完要记得删掉,不然正式环境每页只显示 1 条过去的活动。
延伸 / 后续讲座会用到
Section 7 到这里,Event 这条线(注册、展示、摘要、自定义字段、排序过滤、归档、过去活动)算是完整收尾。接下来会切换到全新的自定义文章类型 Program(专业/学科),并且会讲到 Program 和 Event 之间如何建立关联(post relationship),这是下一个 Section 的内容。
Sources
Udemy:
- Become a WordPress Developer: Unlocking Power With Code — Section 7, EP036