MENU

問い合わせ


    【WordPressで作るキュレーションメディア 第2回】カテゴリ・タグ・「特集」― タクソノミー設計

    記事が100本、200本と増えてくると、「カテゴリが増えすぎて選べない」「同じ意味のタグが3種類ある」「企画でまとめた記事の一覧ページが作れない」といった悩みが出てきます。WordPress の記事の分類は、後から直すほど手間がかかる部分です。

    「カテゴリとタグの使い分けがよく分からない。『生成AI特集』のような企画ページを作りたいときは、どちらを使えばいいの?」

    結論から言うと、分類は「カテゴリ=少数・固定の大分類」「タグ=運用ルール付きの横断キーワード」「特集=編集部が作る企画の束」の3つに役割を分けると、後から崩れにくくなります。特集はカテゴリともタグとも性質が違うため、専用のタクソノミー(=WordPress で記事を分類するための仕組み)をプラグインで追加します。

    前回の第1回では、theme.json に色・文字サイズ・余白のデザイントークンを定義しました。第2回の今回は、分類の設計ルールを決め、「特集」タクソノミーの登録、特集ページのテンプレート、WP-CLI での初期語彙の投入までを実装します。

    目次

    カテゴリ・タグ・特集のタクソノミー設計

    WordPress には標準で「カテゴリ」と「タグ」の2つのタクソノミーがあります。ここに「特集」を加えた3つの役割を、次のように決めました。

    分類役割数の目安1記事あたり誰が作るか
    カテゴリサイトの大きな区分。パンくずやメニューの基準5〜7程度で固定1つ管理者(サイト設計の変更として扱う)
    タグ製品名・制度名などの固有名詞で、カテゴリをまたいで記事を探す運用ルールで管理0〜5つ書き手(ルールに沿って)
    特集「生成AI特集」のような編集部の企画単位の束企画の数だけ0〜2つ編集者以上

    カテゴリは「少数・固定・1記事1つ」

    カテゴリはサイトの骨組みです。メニュー、パンくずリスト(=現在地を示すリンクの列)、URL の設計の基準になるため、頻繁に増やしたり名前を変えたりしないものだけを置きます。

    • 1記事に1つだけ付ける:複数付けると、パンくずや「このカテゴリの記事一覧」の基準があいまいになります。
    • スラッグ(=URL に使う英数字の名前)を最初に決める:第0回の setup.sh で、web-dev、cloud、security、ai-tools、marketing の5つを作っています。
    • 増やすときは「サイト設計の変更」として扱う:メニューやトップページの構成にも影響するため、書き手の判断では増やしません。

    タグは運用ルールとセットで使う

    タグは自由に付けられる反面、ルールがないと「生成AI」「生成AI」「GenAI」のような表記ゆれがすぐに発生します。表記ゆれが増えると、同じ話題の記事が別々の一覧に分かれ、読者も探せなくなります。サンプルでは次のルールを想定しています。

    • タグにするのは固有名詞(製品名・サービス名・制度名など)と、サイトで決めた共通キーワードだけ
    • 表記はタグ一覧に登録済みのものから選ぶ。新しいタグは、記事が複数本見込めるときに作る
    • 英数字は半角、スラッグは英小文字とハイフン
    • 半年に一度など、定期的に「記事が1本しかないタグ」「重複しているタグ」を棚卸しする

    特集は「編集部の企画」を束ねる

    「生成AI特集」は、カテゴリとしては「AI・業務ツール」だけでなく「セキュリティ」の記事も含むかもしれません。一方で、タグのように書き手が自由に作れると、企画として管理できなくなります。

    そこで、特集はカテゴリともタグとも別のタクソノミーとして追加し、作成は編集者以上に限定します。記事への付与は書き手もできるようにし、特集ごとの一覧ページ(/feature/genai/)を持たせます。

    特集タクソノミーをプラグインで登録する

    ここから実装です。今回変更・追加したファイルは次のとおりです。

    ファイル変更内容
    wp-content/plugins/curation-tools/includes/taxonomy-feature.php特集タクソノミーの登録(新規)
    wp-content/plugins/curation-tools/curation-tools.php上記の読み込み、有効化・無効化時の処理、バージョン 0.2.0
    wp-content/themes/curation-child/templates/taxonomy-feature.html特集一覧のテンプレート(新規)
    scripts/seed-terms.sh特集・タグの初期語彙とサンプル記事の投入(新規)

    特集を子テーマではなくプラグインで登録するのは、第0回で決めた「機能はプラグイン」の方針どおりです。タクソノミーをテーマに書くと、テーマを替えた瞬間に特集の分類と URL が消えてしまいます。

    register_taxonomy で「特集」を定義する

    wp-content/plugins/curation-tools/includes/taxonomy-feature.php(ラベルの一部を省略)

    <?php
    if ( ! defined( 'ABSPATH' ) ) {
    	exit;
    }
    
    /**
     * 特集タクソノミーを登録する。
     */
    function curation_tools_register_feature_taxonomy() {
    	$labels = array(
    		'name'          => '特集',
    		'singular_name' => '特集',
    		'all_items'     => 'すべての特集',
    		'add_new_item'  => '新しい特集を追加',
    		// ...(管理画面に表示する文言。省略)
    	);
    
    	register_taxonomy(
    		'feature',
    		array( 'post' ),
    		array(
    			'labels'            => $labels,
    			'description'       => '編集部の企画単位で記事を束ねる分類',
    			'public'            => true,
    			// 親子関係は使わないが true にする。false(タグ型)だと、記事に付けられる人なら
    			// エディターから新しい特集を作れてしまう(REST の作成権限が assign_terms で判定されるため)。
    			// true にするとチェックボックスで「既存の特集から選ぶ」UI になり、作成には edit_terms が必要になる。
    			'hierarchical'      => true,
    			'show_admin_column' => true,
    			'show_in_rest'      => true,
    			'rest_base'         => 'feature',
    			'rewrite'           => array(
    				'slug'       => 'feature',
    				'with_front' => false,
    			),
    			// 特集の作成・編集・削除は編集者以上(manage_categories)、記事への付与は投稿できる人。
    			'capabilities'      => array(
    				'manage_terms' => 'manage_categories',
    				'edit_terms'   => 'manage_categories',
    				'delete_terms' => 'manage_categories',
    				'assign_terms' => 'edit_posts',
    			),
    		)
    	);
    }
    add_action( 'init', 'curation_tools_register_feature_taxonomy' );

    📰 出典:WordPress Developer Resources「register_taxonomy()」

    主な引数の意味は次のとおりです。

    引数値意味
    show_in_resttrueブロックエディターで使うために必須。REST API(/wp-json/wp/v2/feature)にも公開される
    rewritefeature一覧ページの URL を /feature/<スラッグ>/ にする
    show_admin_columntrue管理画面の投稿一覧に「特集」の列を出す
    capabilities上記作成・編集・削除と、記事への付与で必要な権限を分ける

    「階層あり」にしている理由:誰が特集を作れるか

    企画書の段階では、特集はタグと同じ「階層なし」で考えていました。ところが実際に権限を確認すると、階層なしのタクソノミーでは意図どおりになりませんでした。

    WordPress の REST API は、新しい分類(ターム)を作るときの権限を、階層ありなら edit_terms、階層なしなら assign_terms で判定します。タグ型のまま assign_terms を「投稿できる人」にすると、投稿者権限の書き手でもエディターから新しい特集を作れてしまいます。筆者の環境でも、投稿者ユーザーで REST API から特集を作成でき(201)、階層ありに変えると 403 で拒否されることを確認しました。

    そこで、親子関係は使わないものの hierarchical を true にしています。これにより、エディターの特集欄はチェックボックスで「既存の特集から選ぶ」形になり、新しい特集を作れるのは manage_categories 権限を持つ編集者・管理者だけになります。

    有効化時にリライトルールを作り直す

    wp-content/plugins/curation-tools/curation-tools.php(追加部分)

    require_once CURATION_TOOLS_DIR . 'includes/taxonomy-feature.php';
    
    /**
     * 有効化時: タクソノミーを登録してからリライトルール(URL の対応表)を作り直す。
     */
    function curation_tools_activate() {
    	curation_tools_register_feature_taxonomy();
    	flush_rewrite_rules();
    }
    register_activation_hook( __FILE__, 'curation_tools_activate' );
    
    /**
     * 無効化時: 特集の URL を無効にするためリライトルールを作り直す(登録済みの語彙は消さない)。
     *
     * 無効化の処理中もこのファイルは読み込まれているため、先に登録を外してから作り直す。
     */
    function curation_tools_deactivate() {
    	unregister_taxonomy( 'feature' );
    	flush_rewrite_rules();
    }
    register_deactivation_hook( __FILE__, 'curation_tools_deactivate' );

    WordPress は「どの URL をどのページとして扱うか」の対応表(リライトルール)をデータベースに保存しています。新しいタクソノミーを登録しただけでは /feature/genai/ が 404 になるため、有効化のタイミングで一度だけ作り直します。flush_rewrite_rules() は重い処理なので、毎回のページ表示(init の中など)では呼びません。

    📰 出典:WordPress Developer Resources「flush_rewrite_rules()」

    特集一覧のテンプレートを作る

    ブロックテーマでは、templates/taxonomy-<タクソノミー名>.html というファイル名のテンプレートが、そのタクソノミーの一覧ページに自動で使われます。見つからなければ taxonomy.html、archive.html、index.html の順に探されます(テンプレート階層)。

    📰 出典:WordPress Theme Handbook「Template Hierarchy」

    wp-content/themes/curation-child/templates/taxonomy-feature.html(抜粋)

    <!-- wp:template-part {"slug":"header"} /-->
    
    <!-- wp:group {"tagName":"main","style":{"spacing":{"margin":{"top":"var:preset|spacing|60"}}},"layout":{"type":"constrained"}} -->
    <main class="wp-block-group" style="margin-top:var(--wp--preset--spacing--60)">
    	<!-- wp:paragraph {"textColor":"brand","fontSize":"small","style":{"typography":{"fontWeight":"700"}}} -->
    	<p class="has-brand-color has-text-color has-small-font-size" style="font-weight:700">特集</p>
    	<!-- /wp:paragraph -->
    
    	<!-- wp:query-title {"type":"archive","showPrefix":false} /-->
    
    	<!-- wp:term-description /-->
    
    	<!-- wp:query {"query":{"perPage":10,"pages":0,"offset":0,"postType":"post","order":"desc","orderBy":"date","inherit":true},"layout":{"type":"default"}} -->
    	<div class="wp-block-query">
    		<!-- wp:post-template {"layout":{"type":"default"}} -->
    			<!-- (1件分: カテゴリ名・タイトル・抜粋・日付のグループ。省略) -->
    		<!-- /wp:post-template -->
    
    		<!-- wp:query-no-results -->
    			<!-- wp:paragraph -->
    			<p>この特集の記事は準備中です。</p>
    			<!-- /wp:paragraph -->
    		<!-- /wp:query-no-results -->
    
    		<!-- (ページ送り。省略) -->
    	</div>
    	<!-- /wp:query -->
    </main>
    <!-- /wp:group -->
    
    <!-- wp:template-part {"slug":"footer"} /-->

    ポイントは次の3つです。

    • query-title の showPrefix: false:標準では「特集: 生成AI特集」のように接頭辞が付くため、外して見出しを「生成AI特集」だけにしています。代わりに上に小さく「特集」と表示しています(色は第1回の brand トークン)。
    • term-description:管理画面で特集に書いた「説明」を、リード文として表示します。企画の意図や対象読者を書いておく場所として使います。
    • query の inherit: true:表示中のページ(この特集)の条件をそのまま使って記事を並べます。特集ごとにテンプレートを作る必要はありません。

    親テーマの標準の一覧(archive.html)は記事の本文をそのまま並べる作りのため、特集一覧では「カテゴリ・タイトル・抜粋・日付」だけを並べる形にしています。

    WP-CLI で初期語彙を投入する

    特集やタグの初期語彙を管理画面で1つずつ作ると、スラッグの付け忘れや表記ゆれが起きがちです。スクリプトにしておけば、開発環境・ステージング・本番で同じ語彙を再現できます。

    scripts/seed-terms.sh(抜粋)

    wp() { docker compose --progress quiet run --rm -T wpcli wp "$@"; }
    
    create_feature() { # $1=slug $2=name $3=description
      if wp term get feature "$1" --by=slug --field=term_id >/dev/null 2>&1; then
        echo "  skip: $1"
      else
        wp term create feature "$2" --slug="$1" --description="$3" --porcelain >/dev/null
        echo "  created: $1"
      fi
    }
    create_feature genai          '生成AI特集'           '業務で生成AIを使う前に確認したい一次情報と使いどころ'
    create_feature security-basic 'はじめてのセキュリティ対策' '公的機関のガイドラインをもとに、最初にやる対策を整理'
    
    create_tag genai     '生成AI'
    create_tag guideline 'ガイドライン'
    
    # 一覧表示の確認用に、特集・カテゴリ・タグを付けたサンプル記事を3本作る(省略)
    
    wp rewrite flush

    📰 出典:WP-CLI Commands「wp term create」

    スラッグは必ず英小文字で指定しています。日本語の名前だけで作ると、スラッグも日本語になり、URL が %E7%94%9F... のような長い文字列になります。共有されたときに読みにくく、後から変えると URL が変わってしまいます。

    最後の wp rewrite flush は、第0回の setup.sh でプラグインを有効化した時点では特集がまだ無かった環境のために入れています。すでにプラグインが有効な環境では、有効化フックが動かないためです。

    動作確認の方法

    第0回の手順で起動した後、次のように確認します(筆者の環境で確認済みです)。

    ./scripts/seed-terms.sh
    
    # 特集タクソノミーの登録内容(公開・階層)
    docker compose run --rm wpcli wp taxonomy get feature --fields=name,public,hierarchical
    
    # REST API に公開されているか
    curl -s 'http://localhost:8088/wp-json/wp/v2/taxonomies/feature?_fields=slug,rest_base,types'
    curl -s 'http://localhost:8088/wp-json/wp/v2/feature?_fields=slug,name,count'
    
    # 特集ページが 200 で、特集のテンプレートが使われているか
    curl -s -o /dev/null -w '%{http_code}\n' http://localhost:8088/feature/genai/
    curl -s http://localhost:8088/feature/genai/ | grep -o '<body class="[^"]*"'
    • /wp-json/wp/v2/feature に「生成AI特集」「はじめてのセキュリティ対策」が返る
    • /feature/genai/ が 200 で、body の class に tax-feature と term-genai が含まれ、見出しが「生成AI特集」、サンプル記事2本が並ぶ
    • seed-terms.sh をもう一度実行すると、すべて「skip」になる
    • 投稿者権限のユーザーで POST /wp-json/wp/v2/feature を送ると 403、編集者なら作成できる(アプリケーションパスワードを使って確認)

    なお、wp taxonomy get の --fields に show_in_rest は指定できませんでした(WP-CLI 2 では表示できる項目に含まれていないため)。REST への公開は上の curl で確認しています。

    つまずきやすい点

    • 特集ページが 404 になる:リライトルールが古いままです。wp rewrite flush、または管理画面「設定 > パーマリンク」を開いて保存すると作り直されます。
    • スラッグが日本語になる:名前だけ入力するとスラッグが自動で日本語になります。作成時にスラッグ欄を必ず英字で入力するルールにしましょう。
    • カテゴリを後から統合・改名する:カテゴリの URL を変えると、外部からのリンクや検索結果から来た読者が 404 に行き着きます。変更する場合は、古い URL から新しい URL へのリダイレクト(転送)の設定が必要です。
    • プラグインを無効化すると特集ページが表示されなくなる:語彙と記事への付与はデータベースに残りますが、一覧ページは表示されなくなります(WordPress が似た URL へ転送する場合もあります)。無効化は「機能を止める」操作だと理解しておきます。

    発注者向けメモ:分類は後から変えるほど高くつく

    分類の設計は地味ですが、後から変えると「URL の変更」「内部リンクの張り替え」「リダイレクトの設定」「記事の付け直し」がまとめて発生します。記事数が増えるほど、その作業は重くなります。サイトを公開する前に、次の点を決めておきましょう。

    • カテゴリの一覧とスラッグ:5〜7程度に絞り、公開後は原則として変えない前提で決める
    • タグの運用ルール:何をタグにするか、誰が新しいタグを作れるか、表記ゆれをどう防ぐか、棚卸しの頻度
    • 特集など、カテゴリ・タグ以外の分類が必要か:必要なら、誰が作り、一覧ページに何を載せるか
    • URL の形:/category/ のような接頭辞を付けるか、日本語を URL に含めるか

    開発会社に依頼するときは、次のように聞いてみてください。

    • 「カテゴリ・タグ以外の分類を追加する場合、テーマとプラグインのどちらに実装しますか?テーマを変えたら消えませんか?」
    • 「分類ごとに、作成・編集・記事への付与ができる権限を分けられますか?」
    • 「公開後にカテゴリを統合・改名する場合、リダイレクトの設定まで含めてどのくらいの作業になりますか?」
    • 「初期のカテゴリ・タグは、本番とテスト環境で同じものをどうやって作りますか?」

    まとめと次回予告

    第2回では、分類の役割を整理し、特集タクソノミーを実装しました。

    • カテゴリは少数・固定・1記事1つ、タグは運用ルールとセット、特集は編集部の企画単位の束
    • 特集は機能なのでプラグインで register_taxonomy() し、有効化時にリライトルールを作り直す
    • 作成権限を編集者以上に絞るため、親子関係は使わなくても hierarchical を true にした
    • templates/taxonomy-feature.html が特集の一覧ページに自動で使われる
    • 初期語彙は WP-CLI のスクリプトで投入し、どの環境でも同じ状態にする

    次回は「記事・まとめ・ランキングのテンプレートとパターン」です。通常の記事に加えて「まとめ」「ランキング」のページの型を作り、新規投稿時に使えるパターンを登録します。

    この連載の記事一覧

    この記事は連載「WordPressで作るキュレーションメディア」の1回です。連載のほかの回は次のとおりです(連載の一覧ページ)。

    システム制作・運用・保守のお問い合わせはこちら


      よかったらシェアしてね!
      • URLをコピーしました!
      • URLをコピーしました!

      この記事を書いた人

      株式会社THIRD HERO代表取締役 朝野貴朗
      Webシステム開発を中心に、toC向けサービスサイトの運営、ツール開発などを行ってまいりました。

      コメント

      コメント一覧 (2件)

      目次