EP210-211. “批量搭建自定义文章类型模板(archive / single / page)”
🔒 登录后可标记已读📌 并入 EP210 的提醒:跟着这一讲新建各个 Block 的 PHP 文件时,凡是「单篇详情页」场景(singleprogram.php/singleprofessor.php),按 EP208/EP203 学到的规则,不要手写 while (have_posts()) { the_post(); ... }、也不要调用 the_post(),WordPress 6.4+ 的 Block Theme 已经自动处理好了这层循环。如果不确定,随时可以下载这一章任意一讲的参考代码压缩包核对。
按 EP199-211 反复验证过的固定套路,批量补齐自定义文章类型(Program、Professor)和特殊页面(My Notes)各自需要的专属模板——每一个都是「新建 templates/*.html 三段式文件 → functions.php 加一行 new PlaceholderBlock(...) → 复制最简单的占位 JS 改名字 → 新建 PHP 文件、从传统主题对应文件搬运既有代码」。这一讲同时用三个例子巩固了 EP208 的规则:「列表」类模板(programarchive.php,展示全部 Program)保留 while 循环;「单篇详情」类模板(singleprogram.php/singleprofessor.php)不需要外层 while 循环(内部各自独立的关联查询循环——比如相关教授、近期活动——这些不是「主查询循环」,本来就该保留自己的 while);带自定义权限逻辑的特殊页面(mynotes.php,未登录用户重定向)同样不需要主查询循环,但完全保留了自己原有的判断和独立查询逻辑。作者明确表示这类模板会越写越重复,后续没有转换完的模板直接放进参考代码压缩包里,不再逐个录制讲解。
涉及文件(这一讲新增的 4 组 Block + 对应模板)
| Block 名 | 模板文件 | 场景 |
|---|---|---|
programarchive | templates/archive-program.html | Program 列表页(保留 while 循环) |
singleprogram | templates/single-program.html | 单个 Program 详情页(无主循环,内含独立关联查询) |
singleprofessor | templates/single-professor.html | 单个 Professor 详情页(无主循环) |
mynotes | templates/page-my-notes.html | My Notes 功能页(无主循环,保留登录校验 + 独立笔记查询) |
wp-content/themes/fictional-university-block-theme/functions.php(修改,新增 4 个PlaceholderBlock实例)- 对应的
our-blocks/{name}.js/{name}.php各 4 组(新建) templates/archive-{文章类型}.html:命名规则完全沿用传统主题的archive-{文章类型}.php,只是换成.htmltemplates/single-{文章类型}.html:同理对应传统主题的single-{文章类型}.phptemplates/page-{页面slug}.html:同理对应传统主题的page-{slug}.php(比如page-my-notes.php)
代码实现
模板文件三段式(以 archive-program.html 为例,其余同构):
<!-- wp:ourblocktheme/header /-->
<!-- wp:ourblocktheme/programarchive /-->
<!-- wp:ourblocktheme/footer /-->
functions.php:批量追加占位 Block:
new PlaceholderBlock("programarchive");
new PlaceholderBlock("singleprogram");
new PlaceholderBlock("singleprofessor");
new PlaceholderBlock("mynotes");
our-blocks/programarchive.php(列表页,保留 while 循环):
<?php
pageBanner(array(
'title' => 'All Programs',
'subtitle' => 'There is something for everyone. Have a look around.'
));
?>
<div class="container container--narrow page-section">
<ul class="link-list min-list">
<?php
while(have_posts()) {
the_post(); ?>
<li><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></li>
<?php }
echo paginate_links();
?>
</ul>
</div>
our-blocks/singleprogram.php(单篇详情页,不需要主循环,但保留原有的关联教授/近期活动/关联校区三段独立查询):
<?php
pageBanner();
?>
<div class="container container--narrow page-section">
<div class="metabox metabox--position-up metabox--with-home-link">
<p><a class="metabox__blog-home-link" href="<?php echo get_post_type_archive_link('program'); ?>"><i class="fa fa-home" aria-hidden="true"></i> All Programs</a> <span class="metabox__main"><?php the_title(); ?></span></p>
</div>
<div class="generic-content"><?php the_field('main_body_content'); ?></div>
<?php
$relatedProfessors = new WP_Query(array(
'posts_per_page' => -1,
'post_type' => 'professor',
'orderby' => 'title',
'order' => 'ASC',
'meta_query' => array(
array(
'key' => 'related_programs',
'compare' => 'LIKE',
'value' => '"' . get_the_ID() . '"'
)
)
));
if ($relatedProfessors->have_posts()) {
echo '<hr class="section-break">';
echo '<h2 class="headline headline--medium">' . get_the_title() . ' Professors</h2>';
echo '<ul class="professor-cards">';
while($relatedProfessors->have_posts()) {
$relatedProfessors->the_post(); ?>
<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>
<?php }
echo '</ul>';
}
wp_reset_postdata();
// ...后面还有近期活动、关联校区两段类似的独立查询,逻辑与传统主题一致,此处从略...
?>
</div>
our-blocks/mynotes.php(保留登录校验 + 自己的笔记列表查询,同样不需要主循环):
<?php
if (!is_user_logged_in()) {
wp_redirect(esc_url(site_url('/')));
exit;
}
pageBanner();
?>
<div class="container container--narrow page-section">
<div class="create-note">
<h2 class="headline headline--medium">Create New Note</h2>
<input class="new-note-title" placeholder="Title">
<textarea class="new-note-body" placeholder="Your note here..."></textarea>
<span class="submit-note">Create Note</span>
<span class="note-limit-message">Note limit reached: delete an existing note to make room for a new one.</span>
</div>
<ul class="min-list link-list" id="my-notes">
<?php
$userNotes = new WP_Query(array(
'post_type' => 'note',
'posts_per_page' => -1,
'author' => get_current_user_id()
));
while($userNotes->have_posts()) {
$userNotes->the_post(); ?>
<li data-id="<?php the_ID(); ?>">
<input readonly class="note-title-field" value="<?php echo str_replace('Private: ', '', esc_attr(get_the_title())); ?>">
<span class="edit-note"><i class="fa fa-pencil" aria-hidden="true"></i> Edit</span>
<span class="delete-note"><i class="fa fa-trash-o" aria-hidden="true"></i> Delete</span>
<textarea readonly class="note-body-field"><?php echo esc_textarea(get_the_content()); ?></textarea>
<span class="update-note btn btn--blue btn--small"><i class="fa fa-arrow-right" aria-hidden="true"></i> Save</span>
</li>
<?php }
?>
</ul>
</div>
关键改动点:
- 判断「要不要保留 while 循环」的关键不是「文件长不长」,而是「这段循环是不是 WordPress 的主查询循环」:
programarchive.php的while(have_posts())是主查询(列出全部 Program),必须保留;singleprogram.php里的$relatedProfessors/$homepageEvents各自都是开发者自己另外发起的new WP_Query()独立查询,跟 EP208 讲的「主查询循环」完全是两回事,这些独立查询自己的while循环永远都要保留(不管是不是单篇页面)——EP208 的规则只针对页面本身的主查询 mynotes.php保留了完整的原有逻辑:is_user_logged_in()判断 +wp_redirect()未登录跳转、$userNotes = new WP_Query(...)查询当前登录用户自己的笔记——这些都是跟「单篇内容主循环」无关的自定义逻辑,全部原样保留,只是因为整个页面本身是「单篇」性质(一个固定 slug 的页面),所以没有主查询循环需要处理- 文件命名规则完全沿用传统主题的既有约定,只是扩展名换成
.html:archive-{文章类型}、single-{文章类型}、page-{slug}——这三种模式覆盖了 WordPress 模板层级里最常用的几种「更具体」模板类型,跟传统主题的经验高度重合,不需要重新学一套新规则 - 作者明确表示不会再逐个演示剩余模板:这一讲之后如果还需要活动(event)、校区(campus)等自定义文章类型各自的模板,做法完全一样(复制套路即可),课程配套的参考代码压缩包会包含转换好的全部模板文件,不再占用视频时间逐一演示
常见坑
- 单篇详情页的模板 PHP 文件顶部继续保留主查询的
while (have_posts()) { the_post(); ... }——按 WordPress 6.4+ 的新行为会导致页面空白/报错 - 误把「页面内部所有的
while循环」都当成要删除的对象——只有主查询的循环需要去掉,任何开发者自己发起的独立WP_Query查询循环都要正常保留 - 带权限校验/自定义查询的特殊页面(比如 My Notes),生搬硬套「转换成 Block Theme 就要用核心 Block」的思路——这类页面本身没有让终端用户自定义排版的价值,直接原样保留已经调试好的 PHP 逻辑即可
[截图:前台 Program 详情页正确渲染出正文内容和下方"XX Professors"关联教授卡片列表]
延伸 / 后续讲座会用到
下一讲要讲如何限制「全站编辑器」和「普通文章/页面编辑器」里各自能使用哪些 Block 类型。
Sources
Udemy:
- Become a WordPress Developer: Unlocking Power With Code — Section 28, EP210, EP211