0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

WordPressで「公開したのにカードのカテゴリが別カテゴリに化ける」原因と対策

0
Posted at

環境

  • WordPress 6.5.x(クラシックテーマ / 自作)
  • PHP 8.2
  • カスタム投稿タイプ work(制作実績)
  • カスタムタクソノミー work_cat
  • 一覧カードのバッジは post_metawork_display_category を参照
  • ACFは使わず update_post_meta() を直接利用
  • 公開操作は管理画面の「公開」ボタン(REST API経由)

何が起きたか

制作実績一覧サイトで、下書きを「公開」した直後、一覧カードのバッジが想定と別カテゴリで表示されました。具体的には「Web制作」で出すべきカードが「その他」になっていました。

  • 記事編集画面のタクソノミー(work_cat)は正しく「Web制作」が選択済み
  • しかし一覧カードは「その他」
  • 個別ページにはカテゴリ名が出ていないので、一覧を見るまで誰も気づけない
  • 公開から数日間、誤った分類が載ったままになった

つまり DB上は記事が正しいのに、表示だけが別カテゴリ という状態です。これが一番厄介で、編集画面をいくら見ても原因が見えません。

原因:post_meta は publish しても自動で入らない

バッジの描画は get_post_meta()work_display_category を取得し、空だったら else で既定値にフォールバック する実装でした。

<?php
// 一覧カード用テンプレート(改修前)
$args = [
    'post_type'      => 'work',
    'posts_per_page' => 12,
    'post_status'    => 'publish',
];
$q = new WP_Query($args);
?>
<ul class="work-list">
<?php while ($q->have_posts()) : $q->the_post(); ?>
    <?php
    $cat = get_post_meta(get_the_ID(), 'work_display_category', true);
    if ($cat !== '') {
        $label = $cat;
    } else {
        $label = 'その他'; // ← ここが「既定の別カテゴリ」として表示される
    }
    ?>
    <li class="card">
        <span class="badge"><?php echo esc_html($label); ?></span>
        <a href="<?php the_permalink(); ?>"><?php the_title(); ?></a>
    </li>
<?php endwhile; wp_reset_postdata(); ?>
</ul>

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

  • publish はあくまで post_statuspublish に変えるだけ。post_meta誰も入れない限り空のまま
  • テンプレート側が「空 = その他」というフォールバックを持っているため、エラーではなく正常な見た目で誤分類が表示される

タクソノミー(work_cat)と表示用メタ(work_display_category)を分けていたのも遠因です。「タクソノミーを選んだのだから表示も追従するはず」という思い込みが、メタ投入工程の抜けを隠していました。

調査手順

まず「どの公開記事にメタが無いか」をSQLで洗い出します。

SELECT p.ID, p.post_title, p.post_status
FROM wp_posts AS p
LEFT JOIN wp_postmeta AS m
  ON m.post_id = p.ID AND m.meta_key = 'work_display_category'
WHERE p.post_type = 'work'
  AND p.post_status = 'publish'
  AND m.meta_id IS NULL;

WP-CLI が使える環境ならこちらが速いです。

# 個別に確認
wp post meta get 123 work_display_category

# 公開済みの実績を一覧
wp post list --post_type=work --post_status=publish --format=table

wp post meta get が空を返したら、その記事はフォールバック表示になっています。つまり 「メタが空の公開記事 = 誤分類カード」 と同義です。

対策1:公開時にメタを自動投入する

save_post ではなく transition_post_status を使うのがミソです。下書き→公開の遷移を1回だけ拾えるので、編集のたびに走る save_post より意図が明確になります。

add_action('transition_post_status', function ($new_status, $old_status, $post) {
    if ($post->post_type !== 'work') {
        return;
    }
    if ($new_status !== 'publish') {
        return;
    }
    // 既に値があれば上書きしない(編集時の意図を壊さない)
    if (get_post_meta($post->ID, 'work_display_category', true) !== '') {
        return;
    }

    $terms = wp_get_post_terms($post->ID, 'work_cat', ['fields' => 'names']);
    $label = !empty($terms) ? $terms[0] : '未分類';
    update_post_meta($post->ID, 'work_display_category', $label);
}, 10, 3);

$old_status を見て draft 以外からの遷移を弾く厳密化もできますが、まずは「空なら入れる」だけで事故はかなり減ります。

対策2:既存データを一括シードする

フックを後から足しても、過去の公開記事は空のままです。WP-CLI コマンドを用意して一括投入します。

if (defined('WP_CLI') && WP_CLI) {
    WP_CLI::add_command('work seed-category', function () {
        $q = new WP_Query([
            'post_type'      => 'work',
            'post_status'    => 'publish',
            'posts_per_page' => -1,
            'meta_query'     => [[
                'key'     => 'work_display_category',
                'compare' => 'NOT EXISTS',
            ]],
        ]);

        foreach ($q->posts as $p) {
            $terms = wp_get_post_terms($p->ID, 'work_cat', ['fields' => 'names']);
            $label = !empty($terms) ? $terms[0] : '未分類';
            update_post_meta($p->ID, 'work_display_category', $label);
            WP_CLI::log("seeded: {$p->ID} => {$label}");
        }
        WP_CLI::success('done');
    });
}

実行は wp work seed-category。冪等なので何度流しても安全です。

対策3:テンプレートのフォールバックを「安全側」に倒す

根本原因はメタ投入漏れですが、漏れたときに別カテゴリへ化ける のが本当の損害です。フォールバックは「未分類」など明らかに異常と分かる値にし、可能なら一覧から除外します。

<?php
// メタ未設定の記事は一覧に出さない(空バッジで公開してしまう事故を防ぐ)
$q = new WP_Query([
    'post_type'      => 'work',
    'posts_per_page' => 12,
    'post_status'    => 'publish',
    'meta_query'     => [[
        'key'     => 'work_display_category',
        'value'   => '',
        'compare' => '!=',
    ]],
]);

「表示されない」という症状は気づきやすいので、誤分類より遥かにマシです。

対策の比較

対策 実装コスト 漏れ耐性 既存データ対応
transition_post_status で自動投入 高(新規公開のみ) 不可
WP-CLI で一括シード 中(実行忘れ)
フォールバックを安全側に変更 極小
公開フローに手動チェックを追加 極小 低(属人化)

結論は 自動投入+一括シード+安全側フォールバックの3点セット です。どれか1つでは穴が残ります。

FAQ

Q. save_post ではダメですか?
A. ダメではありませんが、記事を編集するたびに発火します。「公開時だけ」を表現したいなら transition_post_status が素直です。両方使うなら重複防止のガードを入れてください。

Q. ACFを使えば自動で入りますか?
A. フィールドに値が入っていなければ同じです。空のフィールドは get_post_meta() で空文字が返るため、フォールバックが働きます。

Q. get_post_meta() の第3引数は?
A. true で単一値、false で配列を返します。カード表示のように単一値を期待する箇所で false のままだと配列になり、esc_html() で警告が出ることがあります。

Q. ブロックエディタやREST API経由の公開でも同じですか?
A. 同じです。post_statuspublish になるだけでメタは自動投入されません。

公開フローのチェックリスト

  • 表示用メタが空の公開記事をSQLで洗い出す
  • transition_post_status で自動投入を実装
  • WP-CLIの一括シードを用意し、既存記事に適用
  • フォールバック値を「未分類」など異常と分かる値に変更
  • READMEに「publishは公開作業の開始点」と明記

まとめ

publish ボタンは 公開作業の完了点ではなく開始点 です。メタ投入まで含めて完了条件に数える。これを運用ルールとして言語化しておくと、同じ事故はまず起きません。テンプレートの else は便利ですが、「空を別の意味に翻訳する」フォールバックは誤りを正常表示に変えてしまう という点を忘れないようにしたいところです。


この記事を書いた人

BENTEN Web Works — 業務自動化・システム開発のフリーランスエンジニアです。

GAS / Python / RPA を使った業務自動化や、Web制作・システム開発のご相談を承っています。
「こんなこと自動化できる?」というご質問だけでもお気軽にどうぞ。

👉 BENTEN Web Works — 詳細・お問い合わせはこちら
🐦 X(旧Twitter) — 日々の知見を発信中

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?