WordPressでプラグインを使わずにカスタム投稿タイプを作る完全ガイド 【最新版】


「通常のブログ投稿では管理しきれない」「商品情報と記事を分けて管理したい」「スタッフ紹介を専用の形式で表示したい」— WordPressサイトを運営していると、こんな課題に直面します。 カスタム投稿タイプ(Custom Post Type, CPT)は、こうした悩みを解決するWordPressの標準機能です。投稿・固定ページに加えて「商品」「実績」「スタッフ」など、コンテンツの性質に応じた専用の投稿タイプを作成できます。 このガイドでは、基本的な作成方法から実務で使える高度な設定、よくあるトラブルの解決方法まで、カスタム投稿タイプの全てを網羅的に解説します。コードをコピペするだけで動く実践的な内容で、明日からすぐに活用できます。
導入(誰の・どんな悩みを、どう解決するか/前提)
WordPressでサイトを運営していると、通常の「投稿」や「固定ページ」だけでは表現しきれないコンテンツが出てきます。例えば、企業サイトなら「実績」「サービス」「スタッフ紹介」、ECサイトなら「商品」、ポートフォリオサイトなら「作品」など、それぞれに独自の項目や表示方法が必要になります。
このガイドは、WordPressのカスタム投稿タイプ(Custom Post Type, CPT)を使って、これらの悩みを解決したいWeb制作者・サイト運営者向けの実践的なマニュアルです。
こんな方におすすめ:
- WordPress案件でクライアントの要望に応えたい制作者
- 複数の投稿タイプを効率的に管理したいサイト運営者
- プラグインに頼らずコードでカスタマイズしたい開発者
前提条件:
- WordPressの基本操作ができる
- PHPの基本構文が理解できる
- functions.phpの編集ができる
カスタム投稿タイプとは?(概念・投稿/固定ページとの違い・活用例)
カスタム投稿タイプは、WordPressの標準的な「投稿」「固定ページ」に加えて、独自のコンテンツタイプを作成する機能です。
標準タイプとの違い:
項目 | 投稿 | 固定ページ | カスタム投稿タイプ |
---|---|---|---|
用途 | ブログ記事・ニュース | 企業情報・プライバシーポリシー | 商品・実績・スタッフなど |
階層 | なし | あり | 設定可能 |
カテゴリー・タグ | 標準で利用可能 | なし | カスタムタクソノミーで作成 |
アーカイブページ | 自動生成 | なし | 設定可能 |
管理画面での表示位置 | 固定 | 固定 | カスタマイズ可能 |
具体的な活用例:
- 企業サイト:実績、サービス、スタッフ紹介、お客様の声
- ECサイト:商品、ブランド、特集
- メディアサイト:レビュー、インタビュー、特集記事
- ポートフォリオ:作品、クライアント、スキル
なぜ必要か(運用/SEO/UXの観点)
運用面のメリット: 通常の投稿と分離することで、管理画面が整理され、コンテンツの更新・管理が効率化されます。編集者が迷わず、適切なコンテンツタイプで投稿できるようになります。
SEO面のメリット: コンテンツごとに最適化されたURL構造(/products/product-name/
など)を作成でき、検索エンジンにとって理解しやすいサイト構造を構築できます。
UX面のメリット: ユーザーが求める情報にアクセスしやすいナビゲーション構造を作れ、コンテンツごとに最適化されたテンプレートでの表示が可能になります。
よくある勘違い(短く正す)
❌ 勘違い1:「カスタム投稿タイプは重い」
→ ⭕ WordPressの標準機能なので、適切に実装すればパフォーマンスへの影響は微小です。
❌ 勘違い2:「プラグインでしか作れない」
→ ⭕ register_post_type()
関数で簡単にコードベースで作成できます。
❌ 勘違い3:「テーマ変更で消える」
→ ⭕ functions.phpやプラグインで作成すれば、テーマに依存しません。
最小構成でCPTを作る
手順(番号付き・クリック単位)
- WordPressの管理画面にログイン
- 「外観」→「テーマエディター」をクリック
- 使用中のテーマの
functions.php
を選択 - ファイル末尾に以下のコードを追加
- 「ファイルを更新」をクリック
- 管理画面左メニューで新しい投稿タイプを確認
コード(最小)+「なぜ」
function create_custom_post_type() {
register_post_type('product',
array(
'labels' => array(
'name' => '商品',
'singular_name' => '商品'
),
'public' => true,
'has_archive' => true,
)
);
}
add_action('init', 'create_custom_post_type');
Code language: PHP (php)
なぜこのコードなのか:
register_post_type()
:WordPressにカスタム投稿タイプを登録する関数'product'
:投稿タイプのスラッグ(URL や内部処理で使用)'labels'
:管理画面で表示される名前'public' => true
:フロントエンドで表示可能にする'has_archive' => true
:一覧ページを自動生成するadd_action('init')
:WordPressの初期化時に実行する
動作確認(UI/URL/成功の見え方)
1. 管理画面での確認:
- 左メニューに「商品」が表示される
- 「商品」をクリックして投稿一覧画面が開く
- 「新規追加」で新しい商品を作成できる
2. フロントエンドでの確認:
https://yoursite.com/product/
でアーカイブページにアクセス- 個別の商品ページは
https://yoursite.com/product/商品名/
で表示 - テーマに
archive-product.php
、single-product.php
がなくても基本テンプレートで表示される
3. 成功の見え方:
- エラーメッセージが出ない
- 管理画面に新しい投稿タイプが表示される
- 投稿の作成・編集ができる
実務で使えるCPT設定
supports/has_archive/rewrite/menu_iconの要点
実際の案件では、より詳細な設定が必要になります。
function create_advanced_post_type() {
register_post_type('portfolio',
array(
'labels' => array(
'name' => 'ポートフォリオ',
'singular_name' => 'ポートフォリオ',
'add_new' => '新規追加',
'add_new_item' => '新規ポートフォリオを追加',
'edit_item' => 'ポートフォリオを編集',
'new_item' => '新規ポートフォリオ',
'view_item' => 'ポートフォリオを表示',
'search_items' => 'ポートフォリオを検索',
'not_found' => 'ポートフォリオが見つかりません',
'not_found_in_trash' => 'ゴミ箱にポートフォリオはありません'
),
'public' => true,
'has_archive' => true,
'menu_icon' => 'dashicons-portfolio',
'supports' => array('title', 'editor', 'thumbnail', 'excerpt', 'custom-fields'),
'rewrite' => array('slug' => 'works'),
'show_in_rest' => true, // Gutenberg対応
'menu_position' => 5
)
);
}
add_action('init', 'create_advanced_post_type');
Code language: PHP (php)
各設定の要点:
- supports:投稿編集画面で利用可能な機能を指定
- has_archive:
true
で一覧ページ作成、文字列でカスタムスラッグ指定 - rewrite:URL構造をカスタマイズ(
slug
でパスを変更) - menu_icon:管理画面での表示アイコン(Dashiconsを使用)
- menu_position:メニューでの表示位置
- show_in_rest:REST API対応(Gutenberg使用時は必須)
階層型CPT(必要かどうかの判断軸)
階層型投稿タイプは'hierarchical' => true
で有効になりますが、使用場面を慎重に検討する必要があります。
// 階層型CPTの例
'hierarchical' => true,
'supports' => array('title', 'editor', 'page-attributes'),
'rewrite' => array('slug' => 'service', 'with_front' => false),
Code language: PHP (php)
階層型を使うべき場面:
- サービス > サブサービス のような明確な親子関係がある
- ページ順序が重要(
menu_order
を使用) - 固定ページのような使い方をしたい
階層型を避けるべき場面:
- 投稿数が多い(管理が煩雑になる)
- カテゴリーやタグで十分分類できる
- 時系列での表示が重要
カスタムタクソノミーで分類を追加
register_taxonomyの基本(カテゴリー型/タグ型)
カスタム投稿タイプには、専用の分類システム(カスタムタクソノミー)を追加できます。
// カテゴリー型(階層あり)
function create_portfolio_taxonomy() {
register_taxonomy('portfolio_category', 'portfolio', array(
'labels' => array(
'name' => 'ポートフォリオカテゴリー',
'singular_name' => 'ポートフォリオカテゴリー',
'add_new_item' => '新規カテゴリーを追加',
'edit_item' => 'カテゴリーを編集'
),
'hierarchical' => true, // カテゴリー型
'public' => true,
'show_in_rest' => true,
'rewrite' => array('slug' => 'portfolio-category')
));
}
// タグ型(階層なし)
function create_portfolio_tags() {
register_taxonomy('portfolio_tag', 'portfolio', array(
'labels' => array(
'name' => 'ポートフォリオタグ',
'singular_name' => 'ポートフォリオタグ'
),
'hierarchical' => false, // タグ型
'public' => true,
'show_in_rest' => true,
'rewrite' => array('slug' => 'portfolio-tag')
));
}
add_action('init', 'create_portfolio_taxonomy');
add_action('init', 'create_portfolio_tags');
Code language: PHP (php)
CPTとタクソノミーの紐付け
既存の投稿タイプに新しいタクソノミーを追加する場合:
// 既存のCPTに新しいタクソノミーを紐付け
function add_taxonomy_to_existing_cpt() {
register_taxonomy_for_object_type('portfolio_category', 'portfolio');
register_taxonomy_for_object_type('post_tag', 'portfolio'); // 既存のタグを使用
}
add_action('init', 'add_taxonomy_to_existing_cpt');
Code language: JavaScript (javascript)
活用例:
- 商品 → ブランド、カテゴリー、特徴
- 実績 → 業界、サービス、規模
- スタッフ → 部署、スキル、担当業務
REST APIとGutenberg対応(show_in_rest/テンプレ)
現代のWordPress開発では、REST APIとGutenberg(ブロックエディタ)への対応が不可欠です。
function create_modern_post_type() {
register_post_type('news',
array(
'labels' => array(
'name' => 'ニュース',
'singular_name' => 'ニュース'
),
'public' => true,
'has_archive' => true,
'show_in_rest' => true, // REST API対応(必須)
'rest_base' => 'news-api', // REST APIのエンドポイント名
'rest_controller_class' => 'WP_REST_Posts_Controller',
'supports' => array(
'title',
'editor', // Gutenbergエディタ
'thumbnail',
'excerpt',
'custom-fields'
),
'template' => array(
array('core/heading', array(
'level' => 2,
'placeholder' => 'ニュースタイトルを入力...'
)),
array('core/paragraph', array(
'placeholder' => 'ニュース本文を入力...'
))
),
'template_lock' => false // テンプレートの変更を許可
)
);
}
add_action('init', 'create_modern_post_type');
Code language: PHP (php)
REST API対応のメリット:
- ヘッドレスCMS として利用可能
- JavaScript フレームワークとの連携
- モバイルアプリとのデータ連携
Gutenbergテンプレートの活用:
- 投稿作成時のデフォルトブロック構成を指定
- コンテンツの品質を統一
- 編集者の作業効率化
管理画面のカスタマイズ(カラム追加・並び替え・クイック編集)
カスタム投稿タイプの管理画面をより使いやすくカスタマイズしましょう。
// カスタムカラムの追加
function add_portfolio_columns($columns) {
$new_columns = array();
$new_columns['cb'] = $columns['cb'];
$new_columns['title'] = $columns['title'];
$new_columns['portfolio_category'] = 'カテゴリー';
$new_columns['thumbnail'] = 'サムネイル';
$new_columns['date'] = $columns['date'];
return $new_columns;
}
// カスタムカラムの内容表示
function portfolio_custom_column($column, $post_id) {
switch($column) {
case 'portfolio_category':
$terms = get_the_terms($post_id, 'portfolio_category');
if ($terms && !is_wp_error($terms)) {
$term_names = array();
foreach ($terms as $term) {
$term_names[] = $term->name;
}
echo implode(', ', $term_names);
} else {
echo '未分類';
}
break;
case 'thumbnail':
$thumbnail = get_the_post_thumbnail($post_id, array(80, 80));
echo $thumbnail ? $thumbnail : '画像なし';
break;
}
}
// ソート可能カラムの設定
function portfolio_sortable_columns($columns) {
$columns['portfolio_category'] = 'portfolio_category';
return $columns;
}
add_filter('manage_portfolio_posts_columns', 'add_portfolio_columns');
add_action('manage_portfolio_posts_custom_column', 'portfolio_custom_column', 10, 2);
add_filter('manage_edit-portfolio_sortable_columns', 'portfolio_sortable_columns');
Code language: PHP (php)
クイック編集の拡張:
// クイック編集にカスタムフィールドを追加
function portfolio_quick_edit_fields($column_name, $post_type) {
if ($post_type != 'portfolio') return;
switch($column_name) {
case 'portfolio_category':
?>
<fieldset class="inline-edit-col-right">
<div class="inline-edit-col">
<label>
<span class="title">カテゴリー</span>
<?php
wp_dropdown_categories(array(
'taxonomy' => 'portfolio_category',
'name' => 'portfolio_category',
'show_option_all' => 'カテゴリーを選択'
));
?>
</label>
</div>
</fieldset>
<?php
break;
}
}
add_action('quick_edit_custom_box', 'portfolio_quick_edit_fields', 10, 2);
Code language: JavaScript (javascript)
SEOとパーマリンク設計(命名・rewrite・プラグイン連携)
SEOに配慮したURL構造とプラグイン連携の設定方法を解説します。
function seo_friendly_post_type() {
register_post_type('case_study',
array(
'labels' => array(
'name' => '導入事例',
'singular_name' => '導入事例'
),
'public' => true,
'has_archive' => true,
'rewrite' => array(
'slug' => 'cases',
'with_front' => false, // /blog/cases/ ではなく /cases/ にする
'feeds' => true,
'pages' => true
),
'supports' => array('title', 'editor', 'thumbnail', 'excerpt'),
'show_in_rest' => true,
// Yoast SEO対応
'publicly_queryable' => true,
'query_var' => true,
'capability_type' => 'post'
)
);
}
// カスタムパーマリンク構造
function custom_post_type_permalinks() {
add_rewrite_rule(
'^cases/([^/]+)/?$',
'index.php?case_study=$matches[1]',
'top'
);
}
add_action('init', 'seo_friendly_post_type');
add_action('init', 'custom_post_type_permalinks');
// パーマリンク更新時の処理
function flush_rewrite_rules_on_activation() {
seo_friendly_post_type();
flush_rewrite_rules();
}
register_activation_hook(__FILE__, 'flush_rewrite_rules_on_activation');
Code language: PHP (php)
SEO最適化のポイント:
- URL構造:短く、分かりやすいスラッグを使用
- 階層化:必要に応じてカテゴリーをURLに含める
- 多言語対応:
wpml
やpolylang
との連携を考慮 - 構造化データ:JSON-LD での Schema.org 対応
主要SEOプラグインとの連携:
// Yoast SEO のメタボックスを表示
add_post_type_support('case_study', 'yoast-seo-metabox');
// All in One SEO Pack 対応
function aioseo_custom_post_type_support() {
if (function_exists('aioseo')) {
aioseo()->core->postTypes->addPostTypeSupport('case_study');
}
}
add_action('init', 'aioseo_custom_post_type_support');
Code language: PHP (php)
よくある失敗とQ&A(5件以上:症状→原因→解決)
Q1: カスタム投稿タイプが管理画面に表示されない
症状: functions.php にコードを追加したが、管理画面にメニューが表示されない
原因: public => false
になっている、または show_ui => false
が設定されている
解決:
register_post_type('product', array(
'public' => true, // これを true に
'show_ui' => true, // 明示的に指定
'show_in_menu' => true, // メニュー表示を有効化
// その他の設定...
));
Code language: PHP (php)
Q2: 個別ページが404エラーになる
症状: 一覧ページは表示されるが、個別投稿をクリックすると404エラー
原因: パーマリンク構造が更新されていない
解決:
- WordPressの管理画面で「設定」→「パーマリンク設定」にアクセス
- 何も変更せずに「変更を保存」をクリック
- または、コードで
flush_rewrite_rules()
を実行
// テーマ有効化時に実行
function activate_custom_post_types() {
create_custom_post_type();
flush_rewrite_rules();
}
add_action('after_switch_theme', 'activate_custom_post_types');
Code language: JavaScript (javascript)
Q3: アーカイブページが表示されない
症状: /product/
にアクセスしても404エラーになる
原因: has_archive => false
になっている、またはテンプレートファイルがない
解決:
register_post_type('product', array(
'has_archive' => true, // または 'has_archive' => 'products'
'public' => true,
// その他の設定...
));
Code language: PHP (php)
Q4: Gutenbergで編集できない
症状: クラシックエディタのみが表示され、ブロックエディタが使えない
原因: show_in_rest => false
になっている
解決:
register_post_type('product', array(
'show_in_rest' => true, // REST API対応を有効化
'supports' => array('title', 'editor'), // editor サポートも確認
// その他の設定...
));
Code language: PHP (php)
Q5: カスタムフィールドが表示されない
症状: 投稿編集画面でカスタムフィールドのメタボックスが表示されない
原因: supports
にカスタムフィールドが含まれていない
解決:
register_post_type('product', array(
'supports' => array('title', 'editor', 'custom-fields'), // custom-fields を追加
// その他の設定...
));
Code language: PHP (php)
Q6: 検索結果に表示されない
症状: WordPressの検索機能でカスタム投稿タイプの記事がヒットしない
原因: デフォルトでは検索対象に含まれない
解決:
function include_custom_post_in_search($query) {
if ($query->is_search() && $query->is_main_query() && !is_admin()) {
$query->set('post_type', array('post', 'page', 'product'));
}
}
add_action('pre_get_posts', 'include_custom_post_in_search');
Code language: PHP (php)
Q7: テーマ変更で消えてしまった
症状: テーマを変更したら、カスタム投稿タイプが表示されなくなった
原因: functions.php にコードを記述していたため、テーマと一緒に削除された
解決: プラグイン化するか、子テーマの functions.php に記述
// プラグインとして作成する場合(wp-content/plugins/custom-post-types/custom-post-types.php)
<?php
/*
Plugin Name: Custom Post Types
Description: サイト専用のカスタム投稿タイプ
Version: 1.0
*/
// プラグイン無効化時にデータを保持
register_deactivation_hook(__FILE__, function() {
// 何もしない(データを保持)
});
Code language: PHP (php)
仕上げチェックリスト(読者が自分で確認)
カスタム投稿タイプの実装が完了したら、以下の項目をチェックしてください:
基本機能チェック
- [ ] 管理画面の左メニューにカスタム投稿タイプが表示される
- [ ] 新規投稿を作成できる
- [ ] 投稿を編集・削除できる
- [ ] 一覧ページ(
/post-type-slug/
)が表示される - [ ] 個別ページ(
/post-type-slug/post-name/
)が表示される
URL・SEOチェック
- [ ] URL構造が意図したとおりになっている
- [ ] パーマリンクが正常に動作する
- [ ] 検索エンジンにインデックスされている(
site:yoursite.com post-type-slug
で確認) - [ ] 構造化データが適切に出力されている
管理画面・UXチェック
- [ ] 必要な入力項目(タイトル、エディタ、サムネイルなど)が表示される
- [ ] カスタムタクソノミーが正常に動作する
- [ ] メニューアイコンが適切に表示される
- [ ] 並び順・フィルター機能が動作する
パフォーマンス・セキュリティチェック
- [ ] 不要なクエリが発生していない
- [ ] キャッシュプラグインとの互換性がある
- [ ] 権限設定が適切である
- [ ] エラーログにPHPエラーが出力されていない
レスポンシブ・アクセシビリティチェック
- [ ] モバイル表示で問題なく表示される
- [ ] 適切な HTML構造になっている
- [ ] alt属性、title属性が設定されている
- [ ] フォーカス順序が論理的である
プラグイン・テーマ互換性チェック
- [ ] 使用中のテーマで正常に表示される
- [ ] SEOプラグインが正常に動作する
- [ ] バックアッププラグインでデータが保護される
- [ ] キャッシュプラグインで問題が発生しない
まとめ(学べたこと/次の一歩)
このガイドを通じて、WordPressのカスタム投稿タイプの作成から実用的な設定まで、幅広く学ぶことができました。
学べたこと:
register_post_type()
関数の基本的な使い方- 実務で必要な詳細設定(supports、rewrite、menu_iconなど)
- カスタムタクソノミーとの連携方法
- REST API・Gutenberg対応の重要性
- 管理画面のカスタマイズ手法
- SEOに配慮したURL設計
- よくある問題の解決方法
次の一歩:
- 高度なカスタマイズ
- Advanced Custom Fields(ACF)との連携
- カスタムメタボックスの作成
- 条件分岐による表示制御
- パフォーマンス最適化
- カスタムクエリの最適化
- キャッシュ戦略の実装
- 画像最適化の自動化
- ヘッドレスCMS化
- REST API の拡張
- GraphQL との連携
- Next.js、Nuxt.js などのフロントエンド実装
- チーム開発対応
- バージョン管理(Git)での管理
- 環境別設定の分離
- デプロイ自動化の実装
カスタム投稿タイプをマスターすることで、WordPressの可能性を大幅に広げることができます。クライアントのニーズに柔軟に対応し、保守性の高いサイトを構築していきましょう。