WP DEVELOP

EP036. “新建 Past Events 页面模板,并修复自定义查询下的分页”

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

首页只显示未来的活动,归档页也过滤掉了过去的活动,但如果有访客想回顾学校办过哪些活动呢?这一讲新建一个「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_pagesWP_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