結論
カスタム投稿タイプとは、WordPress 標準の「投稿」「固定ページ」とは別の種類のコンテンツを追加する仕組みです。register_post_type()(WordPress 2.9.0 から)で登録します。
カテゴリーで分けるか、カスタム投稿タイプを作るかは、次の基準で判断すると迷いません。
- 入力項目・表示テンプレート・URL・管理メニューが、通常の投稿と違う → カスタム投稿タイプ(例: 制作実績、商品、求人、FAQ)
- 中身の形は同じで、話題で分けたいだけ → カテゴリーやタグ、独自タクソノミー(例: ニュースを「お知らせ」「プレス」に分ける)
- 1件ごとに付く値(価格・日付など)を持たせたい → カスタムフィールド(投稿メタ)
原因・仕組み
WordPress では、投稿・固定ページ・添付ファイル・リビジョン・ナビゲーションメニューの項目などがすべて wp_posts テーブルに入り、post_type 列で種類を区別しています。カスタム投稿タイプは、この post_type に独自の値を追加するものです。
そのため、次の性質があります。
- 投稿タイプ名(キー)は最大20文字。Plugin Handbook は、
post_type列が 20 文字の VARCHAR だからと説明しています。 post、page、attachment、revision、nav_menu_item、wp_block、wp_templateなどはコアが使う予約名です。action、author、order、themeも避けるよう、リファレンスに書かれています。wp_で始まる名前もコアが使うため避けます(Plugin Handbook)。- 既定のクエリ(
WP_Queryでpost_typeを指定しない場合)はpostだけを対象にするので、カスタム投稿タイプの記事は明示的に指定しないと取得されません。
手順(サンプルコード)
add_action( 'init', function () {
register_post_type(
'wl1_work', // 接頭辞つき・20文字以内
array(
'labels' => array(
'name' => '制作実績',
'singular_name' => '制作実績',
),
'public' => true, // フロント表示・管理画面のメニュー
'show_in_rest' => true, // ブロックエディターを使う
'has_archive' => true, // /works/ の一覧ページを作る
'menu_icon' => 'dashicons-portfolio',
'supports' => array( 'title', 'editor', 'thumbnail', 'excerpt' ),
'rewrite' => array( 'slug' => 'works' ),
)
);
} );
リファレンスは「投稿タイプの登録は init より前にフックしてはいけない」と書いています。登録後に URL が 404 になる場合は、パーマリンク設定を一度保存してリライトルールを更新します(パーマリンク変更後に404になる原因と対処|.htaccess と mod_rewrite)。
最初に決める設定
| 引数 | 既定値 | 決め方 |
|---|---|---|
public |
false |
フロントに出すなら true |
show_in_rest |
false |
ブロックエディター・REST API を使うなら true |
has_archive |
false |
一覧ページ(アーカイブ)が要るなら true |
hierarchical |
false |
親子関係が要るなら true(固定ページ型) |
supports |
title, editor |
アイキャッチ・抜粋などを追加 |
動作確認(検証環境と結果)
WordPress 7.1.2(ja)、PHP 8.2.12 の CLI 検証環境で実行しました。
labelだけで登録した場合:public・show_ui・show_in_rest・has_archiveはすべてfalse、exclude_from_searchはtrue、use_block_editor_for_post_type()はfalse、supportsはtitle, editor, autosaveだった。つまり既定値のままでは管理画面のメニューにも出ず、ブロックエディターも使えません。- 上のサンプルの設定では、
public・show_in_rest・use_block_editor_for_post_type()がtrue、get_post_type_archive_link()はhttp://localhost/works/を返した。 - 21文字のキーでは Notice(「投稿タイプ名の長さは1から20文字でなければいけません」)が出て、戻り値は
WP_Errorだった。 - キーに
postを指定した場合、エラーにはならずWP_Post_Typeが返った(そのリクエスト内で標準の投稿の設定が置き換わる)。予約名は WordPress が止めてくれるとは限らないので、自分で避ける必要があります。 wl1_workの記事を1件作ると、post_typeを指定しない(postの)クエリでは 0 件、'post_type' => 'wl1_work'では 1 件だった。パーマリンクは/works/記事スラッグ/になった。確認後に削除。
ログは testscustom-post-type-vs-category-when-to-use.md にあります。
カスタム投稿タイプの注意点
- Plugin Handbook は、カスタム投稿タイプはテーマではなくプラグインに書くことを勧めています。テーマを変えてもコンテンツを扱えるようにするためです。テーマの functions.php に書くと、テーマ変更で管理画面から記事が見えなくなります(データは残ります)。
show_in_restをfalseのままにすると、旧エディター(クラシック)の編集画面になります。exclude_from_searchの既定値はpublicの逆です。public => trueならサイト内検索の対象になります。- 投稿タイプ名は後から変えにくい値です(既存記事の
post_type列を書き換える必要がある)。最初に慎重に決めます。
カスタム投稿タイプでよくあるミス
- 「カテゴリーで十分な分類」のためにカスタム投稿タイプを増やし、テンプレートと管理メニューが増えて保守が大変になる。
publicを付け忘れて、管理画面のメニューに出てこない。- 一覧ページを
has_archiveなしで作ろうとして、固定ページとクエリを自作してしまう。 - トップページの新着一覧に混ぜたいのに
post_typeを指定していない(posts_per_page が効かない原因と pre_get_posts の正しい書き方 のpre_get_postsで追加できます)。
カスタム投稿タイプのチェックリスト
- [ ] 「中身の形が違う」から投稿タイプを分けるのか確認した
- [ ] キーは接頭辞つき・20文字以内・予約名以外
- [ ]
initで登録している - [ ]
public・show_in_rest・has_archive・supportsを決めた - [ ] プラグイン側に置いた
- [ ] 登録後にパーマリンクを更新した
カスタム投稿タイプのFAQ(よくある質問)
Q. 分類はどうしますか?
A. 投稿タイプ専用の分類は register_taxonomy() で独自タクソノミーを作ります(register_taxonomy() の使い方|説明・引数・注意点)。
Q. テンプレートはどれが使われますか?
A. 個別ページは single-wl1_work.php、一覧は archive-wl1_work.php が優先され、無ければ single.php・archive.php に下がります。条件分岐は カスタム投稿タイプの条件分岐|is_singular・is_single・is_post_type_archive の違い(準備中) を参照してください。
Q. カスタム投稿タイプは SEO に有利ですか?
A. 投稿タイプそのものが評価を変えるわけではありません。URL と一覧ページを整理しやすくなる、という構造上の利点です。
筆者の見解(カスタム投稿タイプ)
カスタム投稿タイプは「分類のための道具」ではなく「入力と表示の型を分けるための道具」だと考えると、作りすぎを防げると私は思います。話題で分けたいだけならカテゴリーやタクソノミーの方が、テンプレートも管理画面も増えず運用が楽です。逆に、入力欄やテンプレートが明らかに違うのにカテゴリーで無理に分けると、条件分岐だらけのテンプレートになります。判断に迷ったら「この種類だけ別の入力欄が要るか」を基準にするのがよいと考えます。
カスタム投稿タイプの関連項目
- register_post_type() の使い方|説明・引数・注意点
- register_taxonomy() の使い方|説明・引数・注意点
- get_post_type() の使い方|説明・引数・注意点
- get_post_type_archive_link() の使い方|説明・引数・注意点
- is_post_type_archive() の使い方|説明・引数・注意点
- カスタム投稿タイプの条件分岐|is_singular・is_single・is_post_type_archive の違い(準備中)
- カスタムタクソノミー アーカイブが404になる原因と対処
- flush_rewrite_rules() の使い方|説明・引数・注意点
- pre_get_posts フックの使い方|説明・引数・注意点

