インデックスの「除外カテゴリー」に該当する投稿を完全に除外する方法

Cocoonカスタマイズ集
この記事は約9分で読めます。
記事内に広告が含まれています。

はじめに

Cocoon設定→インデックス→「除外カテゴリー」を指定すると、そのカテゴリーに属する投稿をインデックスから除外できます。

しかし、WordPressの仕様上、この設定だけでは投稿自体がサイト全体から除外されるわけではありません。
また、除外カテゴリーを設定すると、一般ユーザーだけでなく、ログインユーザーも閲覧できません。

このため、除外カテゴリーに属する投稿を自動的に非公開(private)にし、解除や削除時には再び公開(publish)に戻すことで、除外カテゴリーに属する情報をサイト全体から安全に除外します。

この記事では、この方法について説明します。

もともと「非公開(private)」としていた除外カテゴリーの投稿も、除外カテゴリーの解除や削除によって自動的に「公開(publish)」に戻ります。

実装手順

以下のコードをfunctions.phpに追加します。

/**
 * インデックス除外カテゴリーIDの配列を取得
 *
 * @return int[] 除外カテゴリーIDの配列
 */
if (!function_exists('get_exclude_category_ids')):
function get_exclude_category_ids() {
  $exclude_category_ids = get_archive_exclude_category_ids();

  // 取得値を配列に変換
  if (!is_array($exclude_category_ids)) {
    if (is_string($exclude_category_ids) && !empty($exclude_category_ids)) {
      $exclude_category_ids = explode(',', $exclude_category_ids);
    } else {
      $exclude_category_ids = [];
    }
  }

  // IDを整数型に変換
  return array_map('intval', $exclude_category_ids);
}
endif;


/**
 * 指定された除外カテゴリーに基づいて投稿ステータスを更新
 *
 * @param int $post_id 投稿ID
 * @param int[] $exclude_category_ids 除外カテゴリーIDの配列
 * @return void
 */
if (!function_exists('update_post_status_by_exclude_categories')):
function update_post_status_by_exclude_categories($post_id, $exclude_category_ids) {
  $post = get_post($post_id);

  // 投稿なしは処理対象外
  if (!$post) {
    return;
  }

  $post_cats         = wp_get_post_categories($post_id);
  $post_modified     = $post->post_modified;
  $post_modified_gmt = $post->post_modified_gmt;

  // 公開・非公開以外は処理対象外
  if (!in_array($post->post_status, ['publish', 'private'], true)) {
    return;
  }

  // 投稿ステータスを設定
  $new_status = (!empty($exclude_category_ids) && array_intersect($post_cats, $exclude_category_ids))
    ? 'private'
    : 'publish';

  // ステータスに変更がない場合は終了
  if ($post->post_status !== $new_status) {
    wp_update_post([
      'ID'                => $post_id,
      'post_status'       => $new_status,
      'post_modified'     => $post_modified,
      'post_modified_gmt' => $post_modified_gmt,
    ]);
  }
}
endif;


/**
 * 投稿保存時に除外カテゴリーの設定に基づいて投稿ステータスを制御
 *
 * @param int $post_id 投稿ID
 * @WP_Post $post 投稿オブジェクト
 * @param bool $update 更新かどうか
 * @return void
 */
if (!function_exists('save_post_exclude_category')):
function save_post_exclude_category($post_id, $post, $update) {
  // 投稿タイプが「post」以外の場合は処理対象外
  if ($post->post_type !== 'post') {
    return;
  }

  $exclude_category_ids = get_exclude_category_ids();

  // 除外カテゴリーが設定されていない場合は処理対象外
  if (empty($exclude_category_ids)) {
    return;
  }

  // 無限ループを防ぐため、一時的にsave_postフックを解除
  remove_action('save_post', 'save_post_exclude_category', 10);

  // 除外カテゴリー設定に基づいて投稿ステータスを変更
  update_post_status_by_exclude_categories($post_id, $exclude_category_ids);

  // save_postフックを再登録
  add_action('save_post', 'save_post_exclude_category', 10, 3);
}
endif;


// 投稿データを保存
add_action('save_post', 'save_post_exclude_category', 10, 3);


// Cocoon設定値を保存
add_filter('cocoon_settings_after_save', function ($settings) {
  $exclude_category_ids = get_exclude_category_ids();

  // サイト内のすべての公開・非公開投稿を取得
  $posts = get_posts([
    'post_type'      => 'post',
    'post_status'    => ['publish', 'private'],
    'posts_per_page' => -1,
    'fields'         => 'ids',
  ]);

  // 取得したすべての投稿のステータスを変更
  foreach ($posts as $post_id) {
    update_post_status_by_exclude_categories($post_id, $exclude_category_ids);
  }

  return $settings;
}, 10);


// カテゴリー削除時に関連投稿のステータスを更新
add_action('delete_term', function ($term_id, $tt_id, $taxonomy, $deleted_term) {
  // カテゴリー以外は処理対象外
  if ($taxonomy !== 'category') {
    return;
  }

  $exclude_category_ids = get_exclude_category_ids();

  // 除外設定に含まれないカテゴリーは処理対象外
  if (!in_array((int) $term_id, $exclude_category_ids, true)) {
    return;
  }

  // 現在非公開になっている投稿を取得
  $posts = get_posts([
    'post_type'      => 'post',
    'post_status'    => 'private',
    'posts_per_page' => -1,
    'fields'         => 'ids',
  ]);

  // 取得した投稿のステータスを再設定
  foreach ($posts as $post_id) {
    update_post_status_by_exclude_categories($post_id, $exclude_category_ids);
  }
}, 10, 4);


// アーカイブ除外設定を解除
add_filter('theme_mod_archive_exclude_category_ids', function ($current_mod) {
  // 管理画面以外では除外設定を解除
  if (!is_admin()) {
    $current_mod = '';
  }

  return $current_mod;
});

なぜ「publish」だと除外が難しいのか

Cocoon設定→インデックス →「除外カテゴリー」を設定しても、必ずしも全ての箇所でその投稿が自動的に除外されるわけではありません。

例えば以下のウィジェットでは、除外カテゴリーに指定した投稿を含み、投稿件数もそのままカウントされます。

  • カテゴリー
  • タグクラウド
  • カレンダー
  • アーカイブ

このため、該当ウィジェットをクリックした際にアーカイブページが存在しないと、404エラーになることがあります。

これは、WordPressの仕様上「公開状態(publish)」の投稿は、メインクエリー以外の場所では自動的に除外されないためです。

投稿ステータスごとの除外状況
対象publishprivate
メインクエリーcategory__not_inで除外される自動で除外される
ページ送り除外されない自動で除外される
カテゴリー除外されない自動で除外される
タグクラウド除外されない自動で除外される
カレンダー除外されない自動で除外される
アーカイブ除外されない自動で除外される

個別に除外する方法

前述のとおり、Cocoon設定→インデックス→「除外カテゴリー」 は、あくまで一覧表示(メインクエリー)から投稿を除外するための機能です。
投稿自体は「公開(publish)」のままなので、ウィジェットやアーカイブなどメインクエリー以外の箇所では引き続き表示対象となってしまいます。

そのため、一覧表示だけでなく、ウィジェットなどにも除外設定を反映させたい場合は、個別に処理を追加する必要があります。

それぞれの対応方法については、以下の記事で紹介しています。

限定公開カテゴリーで自動非公開と公開解除する方法

以下で投稿に特定のカテゴリー(例:private)を付けるだけで、自動的に「非公開」に設定し、削除時には「公開」に戻す方法を紹介しました。

仕組みは同じですが、本記事では「Cocoonテーマの除外カテゴリー設定」と連動して動作します。

さいごに

今回紹介した方法を使えば、「除外カテゴリー」設定に連動して、該当する投稿を自動的に非公開(private)に変更できます。
これにより、カテゴリーやウィジェット、タグクラウドなど、WordPress全体から確実に除外され、404エラーの発生も防げます。
除外カテゴリーを解除または削除すれば、投稿は自動的に公開(publish)へ戻るため、管理も容易です。
インデックス制御を自動化したい場合に最適な方法です。

タイトルとURLをコピーしました