WordPress register_post_type:自定义内容类型从注册到前台显示的完整链路

· wordpress · 阅读 17
摘要

public 与三个细分开关的区别、has_archive 和 rewrite 各管什么、注册后 404 为何必须刷新重写规则,附模板层级与分页控制。

register_post_type() 只是开头。注册完后台菜单出现了,前台访问却 404、归档页不显示、编辑器变成经典样式——这些问题分布在注册参数、重写规则、模板选择三个环节里,得逐层捋。

一、注册的最小可用集

add_action('init', function () {
    register_post_type('book', array(
        'labels' => array(
            'name'          => '图书',
            'singular_name' => '图书',
            'add_new_item'  => '新增图书',
        ),
        'public'        => true,
        'has_archive'   => true,
        'rewrite'       => array('slug' => 'books'),
        'supports'      => array('title', 'editor', 'thumbnail', 'excerpt'),
        'show_in_rest'  => true,
        'menu_icon'     => 'dashicons-book',
    ));
});

六七个参数缺一个就可能出问题,下面逐个说清各自管什么。

二、public 与三个细分开关

'public' => true 是个总闸,它会连带把 publicly_queryable、show_ui、show_in_nav_menus 都设为 true。想让类型「后台可管但前台不可访问」,不要用 public => false(那样后台菜单也没了),而应该:

'public'             => true,
'publicly_queryable' => false,   // 前台单页 404

反过来,「前台可访问但不想让用户看到后台菜单」就用 show_ui => false。这两个开关独立于 public,经常被一起用错。

三、has_archive 与 rewrite 是两个东西

参数控制的地址关掉会怎样
rewrite.slug单页 /books/书名单页变成 ?post_type=book&p=1
has_archive列表 /books/归档页 404,只剩单页

常见误解是以为 rewrite 设了就自动有归档页。实际上要先 has_archive => true,再配合 rewrite.slug 决定路径。两者都设成同一个 slug 时,WordPress 内部有优先级处理,但容易出现列表与单页抢路径,建议单页和归档给不同的 slug 前缀。

四、注册后 404:必须刷新重写规则

这是最高频的坑。register_post_type() 只在 init 跑,而重写规则(rewrite rules)是持久化在数据库 options 里的。新注册的 slug 没进规则表,所以访问 404。

三个解法,按推荐度排序:

  1. 后台 → 设置 → 固定链接 → 直接点保存(不用改任何东西),规则会重刷
  2. 代码里判断一次:flush_rewrite_rules(),但必须只在激活时调用一次
  3. 改完后 WP-CLI 执行 wp rewrite flush

第 2 条常见错误写法:

// 错:每次 init 都 flush,性能灾难
add_action('init', function () {
    register_post_type(...);
    flush_rewrite_rules();   // 千万别这样
});

// 对:只在插件激活时执行一次
register_activation_hook(__FILE__, function () {
    // 注册逻辑抽成函数,这里调一次再刷
    my_register_book();
    flush_rewrite_rules();
});

注意 register_activation_hook 里注册的类型要能被 flush 感知,所以得先执行注册函数,再调 flush。顺序反了照样 404。

五、模板层级:前台用哪个文件渲染

自定义类型走的是独立模板链:

单页:single-book.php → single.php → singular.php → index.php
归档:archive-book.php → archive.php → index.php

放进主题根目录即可。子主题里放会优先于父主题。如果两个都不想要、只想改内容不改布局,可以用 filter 拦截:

add_filter('template_include', function ($template) {
    if (is_singular('book')) {
        return get_stylesheet_directory() . '/templates/book-single.php';
    }
    return $template;
});

放主题子目录时需要这样指定,因为自动查找只在主题根目录找。

六、has_archive 的列表分页

归档页默认每页条数跟随「设置 → 阅读」里的全站值。想单独控制:

add_action('pre_get_posts', function ($q) {
    if (is_admin() || !$q->is_main_query()) {
        return;
    }
    if ($q->is_post_type_archive('book')) {
        $q->set('posts_per_page', 12);
    }
});

两个 return 必须有:is_admin() 防止影响后台列表,is_main_query() 防止影响页面里嵌套的 WP_Query。少了任何一个都会连带改坏别处的分页。

七、show_in_rest 与区块编辑器

不设 'show_in_rest' => true,该类型会退回经典编辑器,也没有 REST 接口(前后端分离、Gutenberg 自定义区块都要它)。

想要自定义字段出现在编辑器侧栏,还需要 register_post_meta() 并同样声明 show_in_rest,否则字段在后台存不进去。

八、分类法挂上去

内置分类和标签默认不挂自定义类型,得显式声明,或者自己注册分类法:

// 复用内置分类
'taxonomies' => array('category', 'post_tag'),

// 或注册专属分类法后挂上去
register_taxonomy('book_genre', 'book', array(...));

复用内置分类有个副作用:该类型的文章会出现在普通分类归档页里,有时候不是想要的。

小结

注册自定义类型的四步:init 里注册 → 激活时 flush 一次规则 → 建对应模板文件 → pre_get_posts 调分页。80% 的「注册了却 404」都卡在第二步。