WP DEVELOP

EP086. “search.php 模板与按文章类型分类样式”

首页 WordPress 开发课程 传统搜索(无 JS) · EP086
约 19 分钟· #EP086#传统搜索(无 JS)
🔒 登录后可标记已读

让传统搜索结果页脱离默认的 index.php(博客列表)样式,新建 search.php(WordPress 按命名约定会自动识别这个文件名当作搜索结果模板)。核心思路是用 get_template_part('template-parts/content', get_post_type()) 动态拼文件名,让每种文章类型(post/page/program/professor/campus/event)都能各自匹配一个专属的 template-parts/content-{类型}.php 局部模板,不用写一长串 if/else。顺带处理了搜索词的 XSS 安全转义、零结果提示,以及把搜索表单抽成 searchform.php(WordPress 认的另一个特殊文件名)避免代码重复。


涉及文件

  • wp-content/themes/fictional-university-theme/search.php (新建,复制自 index.php 再修改)
  • wp-content/themes/fictional-university-theme/searchform.php (新建)
  • wp-content/themes/fictional-university-theme/template-parts/content-post.php (新建,从 search.php 里原本的结果 HTML 剪切出来)
  • wp-content/themes/fictional-university-theme/template-parts/content-professor.php (新建)
  • wp-content/themes/fictional-university-theme/template-parts/content-program.php (新建)
  • wp-content/themes/fictional-university-theme/template-parts/content-page.php (新建)
  • wp-content/themes/fictional-university-theme/template-parts/content-campus.php (新建)

代码实现

search.php(完整文件)

<?php

get_header();
pageBanner(array(
  'title' => 'Search Results',
  'subtitle' => 'You searched for &ldquo;' . esc_html(get_search_query(false))  . '&rdquo;'
));
 ?>

<div class="container container--narrow page-section">
<?php
  if (have_posts()) {
    while(have_posts()) {
      the_post();
      get_template_part('template-parts/content', get_post_type());
    }
    echo paginate_links();
  } else {
    echo '<h2 class="headline headline--small-plus">No results match that search.</h2>';
  }

  get_search_form();

?>

</div>

<?php get_footer();

?>

searchform.php(新建,内容跟 EP085 里 page-search.php 手写的表单一致,抽出来给 get_search_form() 统一调用)

<form class="search-form" method="get" action="<?php echo esc_url(site_url('/')); ?>">
  <label class="headline headline--medium" for="s">Perform a New Search:</label>
  <div class="search-form-row">
    <input placeholder="What are you looking for?" class="s" id="s" type="search" name="s">
    <input class="search-submit" type="submit" value="Search">
  </div>
</form>

template-parts/content-post.php(博客文章样式,从原本 index.php/search.php 里剪切出来)

<div class="post-item">
  <h2 class="headline headline--medium headline--post-title"><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h2>

  <div class="metabox">
    <p>Posted by <?php the_author_posts_link(); ?> on <?php the_time('n.j.y'); ?> in <?php echo get_the_category_list(', '); ?></p>
  </div>

  <div class="generic-content">
    <?php the_excerpt(); ?>
    <p><a class="btn btn--blue" href="<?php the_permalink(); ?>">Continue reading &raquo;</a></p>
  </div>

</div>

template-parts/content-professor.php(复用 single-program.php 里教授关联列表的卡片结构)

<div class="post-item">
  <li class="professor-card__list-item">
    <a class="professor-card" href="<?php the_permalink(); ?>">
      <img class="professor-card__image" src="<?php the_post_thumbnail_url('professorLandscape') ?>">
      <span class="professor-card__name"><?php the_title(); ?></span>
    </a>
  </li>
</div>

template-parts/content-program.php / content-page.php / content-campus.php(都是 content-post.php 的简化版:去掉 metabox 作者信息,按钮文案各自不同)

<!-- content-program.php:按钮文案改成 View program -->
<div class="post-item">
  <h2 class="headline headline--medium headline--post-title"><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h2>

  <div class="generic-content">
    <?php the_excerpt(); ?>
    <p><a class="btn btn--blue" href="<?php the_permalink(); ?>">View program &raquo;</a></p>
  </div>

</div>

<!-- content-campus.php:按钮文案改成 View campus,结构跟 content-program.php 一致 -->
<!-- content-page.php:按钮文案保留 Continue reading,结构跟 content-post.php 一致(只是去掉了 metabox 作者信息)-->

content-event.php 沿用课程更早期已经建好的版本,这一讲没有改动。)

关键改动点:

  • get_template_part('template-parts/content', get_post_type()) 是这一讲的核心技巧:第一个参数是固定前缀,第二个参数动态传入当前循环文章的类型,WordPress 会自动拼成 template-parts/content-{类型}.php 去找对应文件,不用写一长串 if/else 判断类型
  • get_search_query(false) 的参数 false 表示关闭默认转义,因为紧接着外面手动包了一层 esc_html()——如果两层都转义,特殊字符会被转义两次显示错乱;反过来,如果要把搜索词直接当 HTML 属性值用(比如塞进 value="..." 里),可以不加 false,让函数自己处理转义,不需要再包 esc_html()
  • 零结果判断用 if (have_posts()) { ... } else { ... } 包住整个 while 循环和分页链接,不是在循环体内部判断——如果查询根本没有结果,have_posts() 从一开始就是 false,直接走 else 分支输出提示文案
  • 表单去重:searchform.php 是 WordPress 认的特殊文件名,get_search_form() 会自动去读取它;page-search.php(EP085 建的搜索入口页)理论上也应该同步改成调用 get_search_form(),但 EP085 建立时留在页面里的那份内联表单代码没有在这一讲被替换掉,等于目前 page-search.phpsearchform.php 里存在同一份表单 HTML 的重复,如果想彻底去重,可以手动把 page-search.php 里的 <form> 也换成 <?php get_search_form(); ?>

[截图:前台 /?s=关键词 的搜索结果页,展示不同文章类型(博客文章/教授/项目等)各自的卡片样式]


Hook / Function 速查

名称类型用途
get_template_part($slug, $name)WP 内建 function{$slug}-{$name}.php 拼文件名并引入局部模板,找不到就退回 {$slug}.php
get_post_type()WP 内建 function获取当前循环文章的类型,动态拼模板文件名的关键
get_search_query($escape)WP 内建 function获取当前搜索关键词,$escapefalse 可关闭默认转义,方便自己控制转义方式
esc_html()WP 内建 function转义字符串成安全的 HTML 文本,防止 XSS
have_posts()WP 内建 function判断当前查询是否还有(或是否有)结果
paginate_links()WP 内建 function生成分页链接
get_search_form()WP 内建 function输出 searchform.php 里定义的搜索表单,WordPress 按约定文件名自动识别

常见坑

  • 直接把用户搜索词原样输出到页面而不转义——恶意访客可以在搜索框里输入 <script> 标签尝试 XSS 攻击,必须用 esc_html()(用于 HTML 文本内容)或让 get_search_query() 自带转义(用于 HTML 属性值)处理,不能漏掉
  • get_search_query()esc_html() 同时使用时忘记把 get_search_query() 的转义关掉(传 false)——两层转义会导致引号、尖括号等特殊字符被转义两次,显示成乱码
  • get_template_part() 的第二个参数写死具体类型(比如直接写 'event')——这样就退化回硬编码判断了,失去了这一讲要做「动态按类型分发模板」的意义

Sources

Udemy:

  • Become a WordPress Developer: Unlocking Power With Code — Section 17, EP086