2026年5月31日

2026年5月31日

WordPressのカスタムタクソノミーが動作しない問題を解決する方法

はじめに

WordPressでカスタムタクソノミー(分類)を追加したのにカテゴリーページのURLにアクセスすると404エラーになる・投稿編集画面にカスタムタクソノミーのメタボックスが表示されない・register_taxonomy()を設定したのに管理画面の投稿一覧でフィルタリングできない・カスタムタクソノミーのスラッグが既存の投稿スラッグと衝突して表示がおかしくなるといった問題は、rewriteルールの未更新・public設定の不備・投稿タイプとの関連付けミスが原因です。

症状・原因

  • register_taxonomy()rewriteスラッグと既存のページスラッグが衝突している
  • register_taxonomy()で対象の投稿タイプを正しく指定していない
  • タクソノミーを追加・変更した後にflush_rewrite_rules()を実行していない
  • show_in_rest => falseのためブロックエディターでタクソノミーが表示されない

解決手順

ステップ1:カスタムタクソノミーの状態を診断する

# 登録されているタクソノミーを確認
wp eval "
\$taxonomies = get_taxonomies([], 'objects');
foreach (\$taxonomies as \$tax) {
    if (in_array(\$tax->name, ['post_tag', 'category', 'post_format', 'nav_menu', 'link_category'], true)) continue;
    echo \$tax->name . PHP_EOL;
    echo '  public: '         . (\$tax->public       ? 'true' : 'false') . PHP_EOL;
    echo '  show_in_menu: '   . (\$tax->show_in_menu ? 'true' : 'false') . PHP_EOL;
    echo '  show_in_rest: '   . (\$tax->show_in_rest ? 'true' : 'false') . PHP_EOL;
    echo '  hierarchical: '   . (\$tax->hierarchical ? 'true' : 'false') . PHP_EOL;
    echo '  object_type: '    . implode(', ', \$tax->object_type) . PHP_EOL;
    echo '  rewrite slug: '   . (\$tax->rewrite ? \$tax->rewrite['slug'] : 'none') . PHP_EOL;
}
"

# タクソノミーのタームを確認
wp eval "
\$terms = get_terms(['taxonomy' => 'product_category', 'hide_empty' => false]);
echo 'Total terms: ' . count(\$terms) . PHP_EOL;
foreach (\$terms as \$term) {
    echo \$term->slug . ': ' . \$term->name . ' (' . \$term->count . ')' . PHP_EOL;
}
"

# スラッグ衝突を確認
wp eval "
global \$wpdb;
\$conflicting = \$wpdb->get_results(
    \"SELECT post_name, post_type FROM {\$wpdb->posts}
     WHERE post_name = 'genre' AND post_status = 'publish'\"  // タクソノミースラッグと同じ名前
);
foreach (\$conflicting as \$row) {
    echo 'Conflict: ' . \$row->post_type . '/' . \$row->post_name . PHP_EOL;
}
"

ステップ2:カスタムタクソノミーを正しく登録する

// functions.php: カスタムタクソノミーの正しい登録
add_action('init', function(): void {
    // 階層あり(カテゴリー型)タクソノミー
    register_taxonomy('product_category', ['product'], [
        'labels' => [
            'name'              => '商品カテゴリー',
            'singular_name'     => '商品カテゴリー',
            'search_items'      => '商品カテゴリーを検索',
            'all_items'         => 'すべての商品カテゴリー',
            'parent_item'       => '親カテゴリー',
            'parent_item_colon' => '親カテゴリー:',
            'edit_item'         => 'カテゴリーを編集',
            'update_item'       => 'カテゴリーを更新',
            'add_new_item'      => '新しいカテゴリーを追加',
            'new_item_name'     => '新しいカテゴリー名',
            'menu_name'         => '商品カテゴリー',
        ],
        'hierarchical'      => true,      // カテゴリー型
        'public'            => true,
        'show_ui'           => true,
        'show_admin_column' => true,      // 投稿一覧にカラムを追加
        'show_in_rest'      => true,      // ブロックエディター対応(必須)
        'query_var'         => true,
        'rewrite'           => [
            'slug'         => 'product-category',  // スラッグ衝突に注意
            'with_front'   => false,
            'hierarchical' => true,
        ],
    ]);

    // 階層なし(タグ型)タクソノミー
    register_taxonomy('product_tag', ['product'], [
        'labels'       => ['name' => '商品タグ', 'singular_name' => '商品タグ'],
        'hierarchical' => false,
        'public'       => true,
        'show_in_rest' => true,
        'rewrite'      => ['slug' => 'product-tag'],
    ]);
});
# タクソノミーを登録後にrewriteをリセット
wp rewrite flush --hard

# タームを作成
wp term create product_category "家電" --slug=appliances
wp term create product_category "衣類" --slug=clothing

# 投稿にタームを設定
wp post term set 42 product_category appliances

ステップ3:タクソノミーのrewriteスラッグ衝突を解決する

// functions.php: スラッグ衝突を避けるためのrewrite設定
add_action('init', function(): void {
    register_taxonomy('genre', ['post'], [
        'public'  => true,
        'rewrite' => [
            // 'genre' というスラッグが既存ページと衝突する場合
            'slug' => 'topic-genre',  // プレフィックスを追加して衝突を回避
        ],
    ]);
}, 0);  // 優先度0で早期に登録

// カスタムタクソノミーのアーカイブURLを修正
add_filter('term_link', function(string $link, WP_Term $term, string $taxonomy): string {
    if ($taxonomy !== 'product_category') {
        return $link;
    }
    // 必要に応じてURLをカスタマイズ
    return $link;
}, 10, 3);

ステップ4:タクソノミーのテンプレートを設定する

// taxonomy-product_category.php: カスタムタクソノミーのアーカイブテンプレート
<?php
$term       = get_queried_object();
$term_name  = $term->name ?? '';
$term_desc  = $term->description ?? '';
get_header();
?>

<main>
    <header class="taxonomy-header">
        <h1><?php echo esc_html($term_name); ?></h1>
        <?php if ($term_desc) : ?>
            <div class="term-description"><?php echo wp_kses_post($term_desc); ?></div>
        <?php endif; ?>
    </header>

    <?php if (have_posts()) : ?>
        <div class="products-grid">
            <?php while (have_posts()) : the_post(); ?>
                <article class="product-card">
                    <h2><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h2>
                    <?php the_excerpt(); ?>
                </article>
            <?php endwhile; ?>
        </div>
        <?php the_posts_pagination(); ?>
    <?php else : ?>
        <p>この分類には投稿がありません。</p>
    <?php endif; ?>
</main>

<?php get_footer(); ?>
// functions.php: タクソノミーのテンプレートを動的に選択
add_filter('template_include', function(string $template): string {
    if (!is_tax('product_category')) {
        return $template;
    }
    $custom = locate_template(['taxonomy-product_category.php', 'taxonomy.php']);
    return $custom ?: $template;
});

ステップ5:タクソノミーとWP_Queryの統合

// functions.php: タクソノミーでフィルタリングするクエリ
$args = [
    'post_type'  => 'product',
    'tax_query'  => [
        'relation' => 'AND',
        [
            'taxonomy' => 'product_category',
            'field'    => 'slug',
            'terms'    => ['appliances', 'clothing'],
            'operator' => 'IN',
        ],
        [
            'taxonomy' => 'product_tag',
            'field'    => 'name',
            'terms'    => ['セール'],
            'operator' => 'IN',
        ],
    ],
];
$query = new WP_Query($args);
# タームのメタデータを管理
wp eval "
// タームにカスタムフィールドを追加
\$term_id = get_term_by('slug', 'appliances', 'product_category')->term_id;
update_term_meta(\$term_id, 'term_image_url', 'https://example.com/image.jpg');
echo 'Term meta updated for term ID: ' . \$term_id . PHP_EOL;
"

# タームを削除・統合
wp term delete product_category 5  # ターム削除
wp eval "wp_delete_term(5, 'product_category', ['default' => 3]);"  # 投稿をターム3に移動

注意事項

  • register_taxonomy()rewrite['slug']で指定するスラッグが既存のページスラッグや投稿スラッグと一致すると、URLの解決順序の問題が発生します。必ずユニークなスラッグを設定してください
  • show_in_rest => falseにするとGutenbergブロックエディターでタクソノミーが表示されません。ブロックエディターを使用する場合は必ずshow_in_rest => trueを設定してください
  • タクソノミーを既存の投稿タイプ(postpage)に関連付ける場合、第2引数に投稿タイプ名を配列で渡してください

まとめ

WordPressカスタムタクソノミー不動作の解決は①get_taxonomies()でタクソノミーの登録状態・設定値確認・get_terms()でタームの存在確認・スラッグ衝突をDBクエリで確認、②add_action('init')内でregister_taxonomy()を登録・public=trueshow_in_rest=trueshow_admin_column=truerewriteスラッグを設定・wp rewrite flush --hardでルールをリセット、③スラッグ衝突はプレフィックスを付けたスラッグで回避・term_linkフィルターでURLをカスタマイズ・優先度0で早期登録、④taxonomy-{taxonomy}.phpテンプレートを作成・get_queried_object()でタームオブジェクトを取得・template_includeフィルターでテンプレートを動的選択、⑤tax_queryでAND/OR条件による複合フィルタリング・update_term_meta()でタームカスタムフィールドを管理の手順で解決します。

お気軽にご相談ください

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