搜索全站
插件 14 分钟阅读

Custom Post Type UI 创建自定义文章类型:参数配置与固定链接

产品、案例、问答都要单独管理?CPT UI 可以零代码创建自定义文章类型。本文逐项讲清关键参数含义、固定链接刷新的正确时机、模板文件命名规则与导出代码做版本化的做法。

U
UFO_PF 2026-04-27 · 阅读 14 分钟
Cu 插件
阅读提示
本文约 5,216 字,预计阅读 14 分钟。文中命令与配置均经过实测,可直接参考执行。

引言:什么都塞进”文章”,后台迟早会乱

WordPress 默认只有”文章”和”页面”两种内容类型,文章的职能是”按时间倒序列出来的内容”,页面是”独立静态内容”。但真实站点远不止这两种:产品需要参数与价格、案例需要客户与时间、问答需要独立归档、招聘需要有效期。

把它们全塞进”文章”再靠分类区分,短期内能用,长期一定会出问题:归档页混在一起、SEO 结构混乱、后台筛选靠人肉记分类、客户找不到入口。自定义文章类型(Custom Post Type,简称 CPT)就是把这些业务对象各自独立成一张表——它们有自己的菜单、自己的归档页、自己的模板文件、自己的权限。

写 CPT 需要调 register_post_type(),参数有三十多个,手写容易漏。Custom Post Type UI(下称 CPT UI)这个插件把参数做成了表单,还带”生成代码”功能,让你可以先在界面上试,稳定后导出代码进版本控制。

原理分析:几个真正影响行为的参数

CPT UI 界面上的字段直接对应 register_post_type() 的参数,下面这几个最容易踩坑:

参数 作用 常见取值
public 总开关,影响前台可见与后台 UI true
publicly_queryable 是否允许前台 URL 访问 true(关闭后前台 404)
has_archive 是否有归档页 /product/ true / 指定 slug
rewrite 固定链接规则,含 slug 与 with_front array( 'slug' => 'product' )
show_in_rest 是否在古腾堡与 REST API 中可用 true(不开就没有区块编辑器)
supports 编辑器支持的功能模块 title,editor,thumbnail,excerpt
taxonomies 挂载的分类法 array( 'product_cat' )
capability_type 权限模型 post(按文章逻辑)/ page
menu_icon 后台菜单图标 dashicons 类名,如 dashicons-cart
exclude_from_search 是否排除出站内搜索 一般 false

固定链接这块要多说一句。注册 CPT 只是往 WordPress 的重写规则表里写入规则,而规则实际是存在数据库 wp_options 的 rewrite_rules 里的。这个值是懒更新的——你不触发刷新,新注册的 CPT 规则就不在表里,前台访问 /product/xxx/ 直接 404,后台却一切正常。这就是”后台能看、前台 404″的根本原因。

刷新规则的代价不小:它会遍历全部规则重新序列化写入,且常常伴随一次数据库写。所以不能在每次 init 上都刷,正确做法是只在”规则可能变化”的时刻刷一次:插件启用/停用时、主题切换时、手动点一次后台「设置 → 固定链接 → 保存更改」。

保姆级实操

第一步:创建文章类型

后台「CPT UI → Add/Edit Post Types」:

  • Post Type Slug:product(只能用小写字母、数字、下划线,不超过 20 个字符,且不能与保留名冲突如 post、page、attachment、revision、nav_menu_item);
  • Plural Label / Singular Label:产品 / 产品;
  • 勾选 Has Archive、Show in REST、Public;
  • Supports 勾 Title、Editor、Thumbnail、Excerpt;
  • Menu Icon 填 dashicons-cart。

保存后左侧菜单出现「产品」,点进去能正常新建。

第二步:刷新固定链接

这是必做的一步,两个办法任选:

# 命令行(推荐,最干净)
wp rewrite flush --hard

或者在后台「设置 → 固定链接」直接点一次「保存更改」(不用改任何内容)。完成后访问 /product/ 应该能看到归档页。

第三步:建分类法并挂载

「CPT UI → Add/Edit Taxonomies」,Taxonomy Slug 填 product_cat,Hierarchical 选 True(像分类目录)或 False(像标签),Attach to Post Type 勾上 product。分类法同样需要刷新固定链接。

第四步:建模板文件

WordPress 的模板层级对 CPT 有专门约定:

single-product.php        → 单个产品页
archive-product.php       → 产品归档页
taxonomy-product_cat.php  → 产品分类页

没有这些文件时,会退回到 single.php / archive.php / index.php。建议至少建 archive-product.php,因为归档页通常要展示字段网格,跟博客列表完全不一样:

<?php
// archive-product.php
get_header(); ?>
<main class="product-grid">
<?php if ( have_posts() ) : while ( have_posts() ) : the_post(); ?>
    <article class="card">
        <a href="<?php the_permalink(); ?>">
            <?php the_post_thumbnail( 'medium', array( 'loading' => 'lazy' ) ); ?>
            <h2><?php the_title(); ?></h2>
        </a>
        <p class="model"><?php echo esc_html( get_post_meta( get_the_ID(), 'model', true ) ); ?></p>
    </article>
<?php endwhile; the_posts_pagination(); else : ?>
    <p>暂无产品。</p>
<?php endif; ?>
</main>
<?php get_footer();

第五步:导出代码,脱离插件

「CPT UI → Tools → Get Code」可以拿到当前配置的 register_post_type() 代码。粘到主题的 functions.php 或更推荐的小型功能插件里,就可以卸载 CPT UI(插件卸了类型还在,因为注册逻辑变成代码了):

add_action( 'init', function () {
    register_post_type( 'product', array(
        'labels'       => array(
            'name'          => '产品',
            'singular_name' => '产品',
            'add_new_item'  => '添加产品',
        ),
        'public'       => true,
        'has_archive'  => true,
        'show_in_rest' => true,
        'menu_icon'    => 'dashicons-cart',
        'supports'     => array( 'title', 'editor', 'thumbnail', 'excerpt' ),
        'rewrite'      => array( 'slug' => 'product', 'with_front' => false ),
    ) );
} );

代码移走后,务必再 wp rewrite flush 一次,并在卸载插件前后各确认一遍归档页可访问。

第六步:只在必要时刷新规则(代码版)

// 只在插件激活时刷一次,避免每次请求都写数据库
register_activation_hook( __FILE__, function () {
    mimangfei_register_product_cpt();   // 先注册
    flush_rewrite_rules();              // 再刷新
} );

register_deactivation_hook( __FILE__, function () {
    flush_rewrite_rules();
} );

常见踩坑

  1. Slug 超过 20 个字符或含大写字母。 register_post_type() 对 slug 有硬性限制(≤20 字符、仅小写字母数字下划线),超限会注册失败且无明显报错。
  2. with_front 没关。 若站点固定链接结构是 /blog/%postname%/,默认 with_front => true 会让产品链接变成 /blog/product/xxx/。想独立就设 'with_front' => false。
  3. exclude_from_search 忘了改。 默认新建的 CPT 是否被搜索包含取决于 public,如果搜不到产品,检查 publicly_queryable 与 exclude_from_search,并用 pre_get_posts 把它并入主查询。
  4. 归档页翻页 404。 归档页 has_archive 为 true 但分页 /product/page/2/ 404,多半是固定链接未刷新,或主题里用了自定义 WP_Query 却没处理 paged 变量。
  5. show_in_rest 关着,古腾堡编辑器打不开或区块保存报错。 需要在 REST 中暴露的 CPT 必须开 show_in_rest => true。
  6. Slug 与已存在的页面冲突。 已经有一个别名为 /product/ 的页面时,CPT 归档会与之打架,访问结果取决于规则优先级,表现非常诡异。改名或删掉冲突页面。
  7. 案例: 某站点上线前在测试环境建好 CPT 并导入了 400 条产品,上线时只导了数据库没导 CPT UI 配置,结果前台 /product/ 全部 404,而后台菜单、列表、编辑页一切正常——因为 CPT 的注册信息存在 wp_posts 里(post_type 为 cptui_post_type 之类的插件存储),没迁过去。补救办法是把配置以代码形式写进主题,或用 CPT UI 的 Import/Export 功能导出 JSON 再导入。教训:CPT 定义属于”结构”,必须像代码一样纳入版本控制,不能只存在于数据库里。

总结

CPT UI 的正确用法是”界面探索、代码落地”:前期用它快速试参数、看效果,稳定后用 Tools → Get Code 导出,把 register_post_type() 写进主题或功能插件,最终让结构脱离插件存在。

行动清单:slug 控制在 20 字符内的小写;has_archive、show_in_rest、supports 按需勾;每次新增/改名后立刻 wp rewrite flush;归档与单页分别建 archive-{slug}.php 和 single-{slug}.php;上线前确认 CPT 配置已在代码里。产品字段怎么做可以接着看 Advanced Custom Fields 字段组教程,字段多了之后别忘了按 ACF get_field 性能优化 里的方法压一下查询次数;如果还想彻底不依赖插件,Meta Box 与 Pods 插件对比 里有另一套思路。

U
UFO_PF作者

专注 WordPress 生态的实战作者,内容以「可复制、可验证」为第一原则。

留下你的想法

邮箱地址不会被公开。带 * 的为必填项。