WordPress 默认只有两种内容:文章和页面。
但一个像样的企业站,内容类型远不止这两种:
- 案例展示:有项目图、客户名、完成时间
- FAQ:问题和答案两个字段
- 团队介绍:姓名、职位、照片、简介
- 产品手册下载:文件名、版本、下载链接
- 客户评价:内容、客户名、公司、评分
如果把这些全塞进「文章」,你会遇到一堆麻烦:它们混在文章列表里、没法单独做列表页、没有专门的字段。
自定义文章类型(Custom Post Type,简称 CPT)就是为了解决这个问题。
一、什么时候该用 CPT
判断标准很简单,问自己三个问题:
- 这类内容会有多条(而不是单独一页)吗?
- 它需要自己的列表页吗?
- 它的字段结构和普通文章不一样吗?
三个都是「是」,就该用 CPT。
反例:「关于我们」只有一页,用页面就够了;「公司简介」「联系方式」这种一次性内容,用页面。
二、怎么建:插件方式(推荐)
用 Advanced Custom Fields(ACF) + Custom Post Type UI 这两个免费插件组合,全程后台操作,不用写代码。
步骤
- 装 Custom Post Type UI → 添加文章类型 → 填名字(比如
case)、复数标签(案例)、单数标签 - 装 ACF → 字段组 → 新建 → 位置规则设为「文章类型 = 案例」
- 给这个字段组加字段:项目图(图片类型)、客户名(文本)、完成时间(日期)
- 后台就能看到「案例」菜单,像发文章一样添加
关键设置:固定链接
建 CPT 时有个 has_archive 选项,建议设为 true,这样会有一个列表页(比如 /case/)。
另外 rewrite 设置 URL 前缀。建议用短一点、语义清楚的,比如 /cases/。
改完 CPT 设置后一定要去「设置 → 固定链接」点一下保存(不用改任何东西)——这会刷新 WordPress 的路由规则。不改的话新页面会 404。这是新手最常卡住的地方。
三、前台怎么显示
方法一:利用模板层级(最规范)
WordPress 有一套模板优先级规则。建这两个文件:
archive-case.php—— 案例列表页模板single-case.php—— 单个案例页模板
命名规则:archive-{文章类型名}.php 和 single-{文章类型名}.php。
如果不建,WordPress 会用默认的 archive.php / single.php,也能显示,但布局和字段展示不会是你想要的。
方法二:用短代码插入任意位置(灵活)
写一个短代码,在文章/页面里用 [cases limit="6"] 调用:
add_shortcode( 'cases', function( $atts ) {
$atts = shortcode_atts( array( 'limit' => 6 ), $atts );
$q = new WP_Query( array(
'post_type' => 'case',
'posts_per_page' => (int) $atts['limit'],
) );
if ( ! $q->have_posts() ) return '';
ob_start();
echo '<div class="case-list">';
while ( $q->have_posts() ) {
$q->the_post();
$client = get_field( 'client_name' );
echo '<div class="case-item">';
echo '<a href="' . get_permalink() . '">' . get_the_title() . '</a>';
if ( $client ) echo '<span>' . esc_html( $client ) . '</span>';
echo '</div>';
}
echo '</div>';
wp_reset_postdata();
return ob_get_clean();
});
注意最后的 wp_reset_postdata()——用完 WP_Query 必须调用它,否则会影响页面上后续的查询(比如侧栏的文章列表显示错乱)。这是个高频 bug。
方法三:用页面构建器(不写代码)
Elementor Pro、Divi 这些页面构建器都有 CPT 的循环模块,选一下字段就能显示。适合不想碰代码的人。
四、不用插件怎么建 CPT
如果你不想多装插件,用代码注册也很简单。
add_action( 'init', function() {
register_post_type( 'case', array(
'labels' => array(
'name' => '案例',
'singular_name' => '案例',
'add_new_item' => '添加案例',
),
'public' => true,
'has_archive' => true,
'rewrite' => array( 'slug' => 'cases' ),
'supports' => array( 'title', 'editor', 'thumbnail' ),
'show_in_rest' => true,
'menu_icon' => 'dashicons-portfolio',
) );
});
几个参数说明:
has_archive:要不要列表页。设 trueshow_in_rest:要不要在 REST API 和 Gutenberg 编辑器里可用。建议设 true,否则新编辑器用不了,API 也读不到supports:这个类型支持哪些功能。title标题、editor正文、thumbnail缩略图、excerpt摘要menu_icon:后台菜单图标,可以用 WordPress 内置的 dashicons
自定义字段(不用 ACF)
// 后台加字段框
add_action( 'add_meta_boxes', function() {
add_meta_box( 'case_meta', '案例信息', 'case_meta_html', 'case', 'normal' );
});
function case_meta_html( $post ) {
$client = get_post_meta( $post->ID, '_client_name', true );
wp_nonce_field( 'case_save', 'case_nonce' );
echo '<label>客户名称</label>';
echo '<input type="text" name="client_name" value="' . esc_attr( $client ) . '" style="width:100%">';
}
// 保存
add_action( 'save_post_case', function( $post_id ) {
if ( ! isset( $_POST['case_nonce'] ) ||
! wp_verify_nonce( $_POST['case_nonce'], 'case_save' ) ) {
return;
}
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) return;
if ( ! current_user_can( 'edit_post', $post_id ) ) return;
if ( isset( $_POST['client_name'] ) ) {
update_post_meta( $post_id, '_client_name',
sanitize_text_field( $_POST['client_name'] ) );
}
});
注意保存函数里的三个检查:nonce 验证、自动保存跳过、权限检查。三个都不能少,否则有安全漏洞(任何人都能改你的数据)。
五、ACF 还是原生字段:怎么选
| ACF 插件 | 原生自定义字段 | |
|---|---|---|
| 上手难度 | 后台点几下 | 要写代码 |
| 字段类型 | 丰富(图片、日期、关联、 repeater) | 基本只有文本 |
| 后台界面 | 好看、好用 | 丑,要自己做 |
| 性能 | 字段多了会稍慢 | 轻量 |
| 依赖 | 插件停用数据还在(存在 postmeta) | 无依赖 |
我的建议:除非你有特殊理由,用 ACF。它的免费版功能已经很强,而且数据是存在 WordPress 原生的 postmeta 表里的,即使插件停用,数据也不会丢——这点很重要,不会被绑架。
六、常见坑
坑 1:新建 CPT 后页面 404
前面说过:去「设置 → 固定链接」点一次保存。90% 的 404 都是这个原因。
如果还不行,检查 has_archive 是否设了 true,以及 rewrite 的 slug 有没有和现有页面冲突(比如已经有了一个叫 /cases/ 的页面)。
坑 2:文章类型的名字冲突
CPT 的名字(case)不能用 WordPress 保留字,比如 post、page、attachment、revision。也不能太长(建议不超过 20 个字符,数据库字段有限制)。
坑 3:字段值取不到
用 ACF 时用 get_field('字段名');用原生时是 get_post_meta($id, '_字段名', true)。
注意原生方式的字段名前面有下划线(如果你存的时候加了)。两个函数不能混用——ACF 存的数据用 get_post_meta 也能取,但 ACF 的复杂字段(图片、关联)取出来的是 ID 不是对象。
坑 4:列表页只显示 10 条
CPT 的列表页默认受「设置 → 阅读 → 每页显示数量」限制。要改的话:
add_action( 'pre_get_posts', function( $q ) {
if ( ! is_admin() && $q->is_main_query() && $q->is_post_type_archive( 'case' ) ) {
$q->set( 'posts_per_page', 24 );
}
});
七、SEO 相关:别让 CPT 变成孤岛
建了 CPT 之后,记得:
- 确保它有列表页(has_archive),并且这个列表页有内容、有标题
- 加到 sitemap 里——大部分 SEO 插件会自动包含公开 CPT,确认一下
- 内链:从首页或其他页面链接到 CPT 列表页,否则搜索引擎可能抓不到
- 每个 CPT 的内容要真的写好——空壳 CPT 页对 SEO 是负担不是帮助
最后
CPT 是把 WordPress 从「博客系统」变成「内容管理系统」的关键一步。
但有个提醒:不要过度使用。有些人建了七八种 CPT,结果后台复杂得自己都记不住。一个站有两三种 CPT 就够了,能合并的就合并。
内容结构的复杂度是有成本的——它体现在你日常发布内容的时间上。