在 WordPress 中创建自定义文章类型
WordPress 自带文章(Posts)和页面(Pages),但当客户需要房产列表目录、员工名册或食谱档案时,这两种内容类型就显得力不从心了。这时就需要用到 WordPress 自定义文章类型(Custom Post Type)——一种一流的内容结构,它能够完美融入 WordPress 现有的数据模型,而无需对默认设置进行任何破解性修改。
核心要点
- 自定义文章类型(CPT)扩展的是共享的
wp_posts数据表,而不是创建单独的表,这使您可以开箱即用地获得管理界面、REST API 端点和模板路由功能。 - 始终在插件中注册 CPT,而不是在主题中,这样无论主题如何更改,内容都能保持可访问。
- 必须将
show_in_rest设置为true才能支持区块编辑器和 REST API 访问。 - 仅在插件激活时刷新重写规则——切勿在每次页面加载时执行。
什么是自定义文章类型?
自定义文章类型(CPT)是您在 WordPress 中注册的一种命名内容类型。尽管名称如此,但它并不是一个独立的数据库表。所有文章类型——无论是内置的还是自定义的——都共享 wp_posts 表,通过 post_type 列进行区分。这是一个重要的概念模型:您是在扩展现有结构,而不是创建一个平行结构。
默认的文章类型包括 post、page、attachment、revision 和 nav_menu_item。当您注册一个像 property 或 recipe 这样的 CPT 时,WordPress 会以同样一致的方式对待它:管理界面、REST API 端点、URL 路由和模板层级都以相同的方式工作。
自定义文章类型 ≠ 自定义字段。 CPT 定义的是内容的类型。自定义字段(通过 ACF 或元数据 API 添加)存储的是附加到该内容的额外数据。这是两个不同的概念。
使用 register_post_type() 注册自定义文章类型
创建 WordPress 自定义文章类型的标准方法是使用 register_post_type() 函数,在 init 钩子上调用。
以下是一个简洁且可用的示例:
add_action( 'init', 'register_property_post_type' );
function register_property_post_type() {
$labels = [
'name' => 'Properties',
'singular_name' => 'Property',
'add_new_item' => 'Add New Property',
'edit_item' => 'Edit Property',
'not_found' => 'No properties found.',
];
$args = [
'labels' => $labels,
'public' => true,
'has_archive' => true,
'supports' => [ 'title', 'editor', 'thumbnail', 'excerpt' ],
'rewrite' => [ 'slug' => 'properties' ],
'show_in_rest' => true,
];
register_post_type( 'property', $args );
}
关键参数说明
public— 使 CPT 在管理后台和前端可见。对于仅供内部使用的类型,设置为false。labels— 控制管理后台中的所有 UI 文本。值得正确填写,以免编辑人员感到困惑。supports— 声明编辑界面上显示哪些编辑器功能。常用值:title、editor、thumbnail、excerpt、custom-fields。has_archive— 在/properties/启用归档页面。WordPress 会查找匹配的archive-property.php模板(或回退到archive.php)。rewrite— 设置 URL 别名。单篇文章将显示在/properties/post-name/。show_in_rest— 将此设置为true。 这会通过 WordPress REST API 公开 CPT,并且是区块编辑器正确处理您的文章类型所必需的。没有它,编辑器会回退到经典界面。
Discover how at OpenReplay.com.
插件 vs. 主题:代码应该放在哪里?
这是一个常见的困惑点。在插件中注册自定义文章类型,而不是在主题中。
如果您的 CPT 注册代码位于 functions.php 中,而客户切换了主题,该类型的所有内容将变得无法访问——不是被删除,而是不可见。一个专用插件(即使是简单的单文件插件)可以使 CPT 在主题更改时保持活动状态。
不要在每次请求时刷新重写规则。 仅调用一次
flush_rewrite_rules()——通过register_activation_hook()在插件激活时调用。在每次init时刷新会导致性能问题。
自定义文章类型的模板层级
对于单个 CPT 条目,WordPress 按以下顺序查找模板:
single-{post-type}.php(例如single-property.php)single.phpsingular.phpindex.php
对于归档页面,遵循:archive-{post-type}.php → archive.php → index.php。
总结
通过将 register_post_type() 正确挂载到 init,启用 show_in_rest,并将注册代码放在插件中,您就有了一个坚实、可移植的基础。从这里开始,您可以添加自定义分类法、通过 ACF 或元数据 API 添加元字段,以及自定义管理列——而不会触及任何在 WordPress 或主题更新时会损坏的内容。
常见问题
内容会保留在 wp_posts 表中,不会被删除。但是,WordPress 不再识别该文章类型,因此这些文章在管理后台和前端都会变得不可见。重新激活插件会立即恢复访问。这就是为什么 CPT 注册应该始终位于专用插件中而不是主题中的原因。
可以。您可以在同一个 init 回调中多次调用 register_post_type(),或使用挂载到 init 的单独回调。单个插件可以注册的自定义文章类型数量没有限制,但每个类型必须有一个不超过 20 个字符的唯一文章类型别名。
这几乎总是意味着需要刷新重写规则。在 WordPress 管理后台访问设置,然后访问固定链接,点击保存更改。这会触发重写刷新。对于插件,在 register_activation_hook() 内调用 flush_rewrite_rules(),这样它会在激活时自动运行。切勿在每次页面加载时刷新。
严格来说不需要,但仍然建议这样做。将 show_in_rest 设置为 true 会通过 WordPress REST API 公开您的文章类型,这对于无头设置、外部集成和未来兼容性很有用。如果您将来切换到区块编辑器,它已经可以正常工作了。
Gain Debugging Superpowers
Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers. Check our GitHub repo and join the thousands of developers in our community.