2026年7月20日

2026年7月20日

WordPressでカスタム投稿タイプを作成する方法

はじめに

「ブログ投稿とは別に「ポートフォリオ」「スタッフ紹介」「製品」などの専用コンテンツを管理したい」——カスタム投稿タイプを使うと、用途に合わせた独自のコンテンツタイプを管理画面に追加できます。

症状・原因

デフォルトのWordPressには「投稿」と「固定ページ」しかありません。異なる種類のコンテンツを管理したい場合はカスタム投稿タイプを作成する必要があります。register_post_type()の引数が多く、どれを設定すべきか分からないことが多いです。

解決手順

ステップ1:register_post_type()でカスタム投稿タイプを登録する

// functions.php: カスタム投稿タイプ「ポートフォリオ」を登録
add_action( 'init', function() {

    $labels = [
        'name'               => 'ポートフォリオ',
        'singular_name'      => 'ポートフォリオ',
        'add_new'            => '新規追加',
        'add_new_item'       => '新しいポートフォリオを追加',
        'edit_item'          => 'ポートフォリオを編集',
        'new_item'           => '新しいポートフォリオ',
        'view_item'          => 'ポートフォリオを表示',
        'view_items'         => 'ポートフォリオ一覧を表示',
        'search_items'       => 'ポートフォリオを検索',
        'not_found'          => 'ポートフォリオが見つかりません',
        'not_found_in_trash' => 'ゴミ箱にポートフォリオはありません',
        'menu_name'          => 'ポートフォリオ',
    ];

    register_post_type( 'portfolio', [
        'labels'              => $labels,
        'public'              => true,       // フロントエンドに表示
        'publicly_queryable'  => true,       // クエリで取得可能
        'show_ui'             => true,       // 管理画面に表示
        'show_in_menu'        => true,       // 管理メニューに表示
        'query_var'           => true,
        'rewrite'             => [ 'slug' => 'portfolio' ], // /portfolio/{slug}/
        'capability_type'     => 'post',
        'has_archive'         => true,       // アーカイブページを有効化
        'hierarchical'        => false,      // 固定ページのような階層構造にしない
        'menu_position'       => 5,          // 管理メニューの位置(5=投稿の下)
        'menu_icon'           => 'dashicons-portfolio', // メニューアイコン
        'supports'            => [
            'title',
            'editor',
            'thumbnail',     // アイキャッチ画像
            'excerpt',
            'custom-fields',
            'revisions',
        ],
        'show_in_rest'        => true,       // ブロックエディタ + REST API に対応
        'rest_base'           => 'portfolio', // REST API のエンドポイント名
    ] );
} );

ステップ2:複数のカスタム投稿タイプをまとめて登録する

// functions.php: 複数のカスタム投稿タイプを配列から一括登録
add_action( 'init', function() {

    $post_types = [
        'staff' => [
            'name'      => 'スタッフ',
            'slug'      => 'staff',
            'icon'      => 'dashicons-businessman',
            'supports'  => [ 'title', 'editor', 'thumbnail' ],
            'archive'   => false,
        ],
        'faq' => [
            'name'      => 'よくある質問',
            'slug'      => 'faq',
            'icon'      => 'dashicons-editor-help',
            'supports'  => [ 'title', 'editor' ],
            'archive'   => true,
        ],
        'news' => [
            'name'      => 'ニュース',
            'slug'      => 'news',
            'icon'      => 'dashicons-megaphone',
            'supports'  => [ 'title', 'editor', 'thumbnail', 'excerpt' ],
            'archive'   => true,
        ],
    ];

    foreach ( $post_types as $key => $config ) {
        register_post_type( $key, [
            'label'          => $config['name'],
            'public'         => true,
            'show_ui'        => true,
            'show_in_menu'   => true,
            'menu_icon'      => $config['icon'],
            'supports'       => $config['supports'],
            'has_archive'    => $config['archive'],
            'rewrite'        => [ 'slug' => $config['slug'] ],
            'show_in_rest'   => true,
        ] );
    }
} );

ステップ3:カスタム投稿タイプのパーマリンクを反映する

// functions.php: プラグイン有効化・登録時にパーマリンクを自動フラッシュ
register_activation_hook( __FILE__, function() {
    // プラグイン有効化時にリライトルールを再生成
    flush_rewrite_rules();
} );

// または init フックで条件付きフラッシュ(重いので頻繁には実行しない)
add_action( 'init', function() {
    // パーマリンク設定画面を保存したタイミングでは自動フラッシュ済み
    // 開発中にURLが404になる場合は管理画面のパーマリンク設定を一度保存する
} );
【パーマリンクが404になる場合の対処】

1. 管理画面 → 設定 → パーマリンク設定
2. 変更せずに「変更を保存」をクリック
→ これでリライトルールが再生成されて404が解消する

【カスタム投稿タイプのURL構成】
has_archive: true の場合:
  一覧: https://example.com/portfolio/
  個別: https://example.com/portfolio/{post-slug}/

has_archive: false の場合:
  個別: https://example.com/portfolio/{post-slug}/
  一覧: なし(アーカイブページが生成されない)

ステップ4:カスタム投稿タイプのテンプレートファイルを作成する

<?php
// archive-portfolio.php: ポートフォリオ一覧ページテンプレート
get_header();
?>

<main class="portfolio-archive">
    <div class="container">
        <h1 class="archive-title">
            <?php post_type_archive_title(); ?>
        </h1>

        <?php if ( have_posts() ) : ?>
            <div class="portfolio-grid">
                <?php while ( have_posts() ) : the_post(); ?>
                    <article class="portfolio-item">
                        <?php if ( has_post_thumbnail() ) : ?>
                            <a href="<?php the_permalink(); ?>">
                                <?php the_post_thumbnail( 'medium' ); ?>
                            </a>
                        <?php endif; ?>

                        <h2 class="portfolio-title">
                            <a href="<?php the_permalink(); ?>"><?php the_title(); ?></a>
                        </h2>

                        <div class="portfolio-excerpt">
                            <?php the_excerpt(); ?>
                        </div>
                    </article>
                <?php endwhile; ?>
            </div>

            <?php the_posts_pagination(); ?>

        <?php else : ?>
            <p>ポートフォリオが見つかりません。</p>
        <?php endif; ?>
    </div>
</main>

<?php get_footer(); ?>
<?php
// single-portfolio.php: ポートフォリオ個別ページテンプレート
get_header();
?>

<main class="portfolio-single">
    <div class="container">
        <?php while ( have_posts() ) : the_post(); ?>
            <article id="post-<?php the_ID(); ?>">
                <h1><?php the_title(); ?></h1>

                <?php if ( has_post_thumbnail() ) : ?>
                    <div class="portfolio-thumbnail">
                        <?php the_post_thumbnail( 'large' ); ?>
                    </div>
                <?php endif; ?>

                <div class="portfolio-content">
                    <?php the_content(); ?>
                </div>

                <!-- カスタムフィールドを表示 -->
                <?php
                $client_name = get_post_meta( get_the_ID(), 'client_name', true );
                $project_url = get_post_meta( get_the_ID(), 'project_url', true );
                ?>

                <?php if ( $client_name || $project_url ) : ?>
                    <dl class="portfolio-meta">
                        <?php if ( $client_name ) : ?>
                            <dt>クライアント</dt>
                            <dd><?php echo esc_html( $client_name ); ?></dd>
                        <?php endif; ?>
                        <?php if ( $project_url ) : ?>
                            <dt>URL</dt>
                            <dd><a href="<?php echo esc_url( $project_url ); ?>" target="_blank" rel="noopener"><?php echo esc_url( $project_url ); ?></a></dd>
                        <?php endif; ?>
                    </dl>
                <?php endif; ?>
            </article>
        <?php endwhile; ?>
    </div>
</main>

<?php get_footer(); ?>

ステップ5:管理画面のカラムをカスタマイズする

// functions.php: カスタム投稿タイプの一覧に列を追加

// 列を追加
add_filter( 'manage_portfolio_posts_columns', function( $columns ) {
    $new_columns = [];
    foreach ( $columns as $key => $value ) {
        $new_columns[ $key ] = $value;
        if ( $key === 'title' ) {
            $new_columns['thumbnail']    = 'サムネイル';
            $new_columns['client_name']  = 'クライアント';
        }
    }
    return $new_columns;
} );

// 列の内容を出力
add_action( 'manage_portfolio_posts_custom_column', function( $column, $post_id ) {
    switch ( $column ) {
        case 'thumbnail':
            if ( has_post_thumbnail( $post_id ) ) {
                echo get_the_post_thumbnail( $post_id, [ 60, 60 ] );
            }
            break;
        case 'client_name':
            $client = get_post_meta( $post_id, 'client_name', true );
            echo $client ? esc_html( $client ) : '—';
            break;
    }
}, 10, 2 );

注意事項

  • register_post_type()initフックで呼び出してください。テーマのfunctions.phpとプラグインの両方で登録できますが、テーマに依存するコンテンツタイプはテーマが変わると消えるリスクがあるため、プラグインでの管理が推奨されます。
  • カスタム投稿タイプを追加した直後にフロントエンドで404になる場合は、パーマリンク設定画面を一度保存してリライトルールを更新してください。flush_rewrite_rules()initフックで毎回呼ぶと重いので、有効化フックでのみ呼ぶようにしてください。
  • show_in_rest: trueを設定しないとブロックエディタが使えません。カスタム投稿タイプでGutenbergを使う場合は必ず設定してください。

まとめ

カスタム投稿タイプは「register_post_type()でラベル・supports・rewriteを設定→パーマリンク設定を保存してリライトルールを更新→archive-{post_type}.phpとsingle-{post_type}.phpでテンプレートを作成→manage_{post_type}_posts_columnsで管理画面列をカスタマイズ」の流れで構築します。関連記事:WordPressでカスタムタクソノミーを作成する方法WordPressでREST APIを使ってデータを取得する方法

お気軽にご相談ください

お見積りへ お問い合わせへ