最深カテゴリを基準に関連記事を取得するWP_Query

2025-8-16 / last up date:2025-8-16 wordpress

実現したい事

個別記事ページに表示させる関連記事を、
カテゴリに階層がある場合、同階層のカテゴリに属する記事のみを表示したい。

スクリーンショット 2025 08 16 182930

このようなカテゴリ階層の場合、「孫カテゴリ1-1-1」の記事の関連記事は「孫カテゴリ1-1-1」のカテゴリに属する記事のみにする。

「子カテゴリ1-1」の記事の関連記事に「孫カテゴリ1-1-1」の記事を含めるかはパラメータで選択できる。

コードサンプル

functions.phpに追加

<?php
/**
 * 投稿についたタクソノミーの中から「最も深いタームID」を返す
 *
 * @param int    $post_id
 * @param string $taxonomy
 * @return int|null  見つからなければ null
 */
function mytheme_get_deepest_term_id( $post_id, $taxonomy = 'category' ) {
	$terms = get_the_terms( $post_id, $taxonomy );
	if ( empty( $terms ) || is_wp_error( $terms ) ) {
		return null;
	}

	$deepest_id    = null;
	$deepest_depth = -1;

	foreach ( $terms as $term ) {
		$depth = count( get_ancestors( $term->term_id, $taxonomy ) );
		if ( $depth > $deepest_depth ) {
			$deepest_depth = $depth;
			$deepest_id    = (int) $term->term_id;
		}
	}
	return $deepest_id ?: null;
}

/**
 * 「最も深いターム」を基準に関連記事の WP_Query を返す
 *
 * @param array $args {
 *   @type int    $post_id          基準投稿(省略時は現在の投稿)
 *   @type string $taxonomy         タクソノミー名(例: 'category', 'blog-cat')
 *   @type string $post_type        投稿タイプ(例: 'post', 'blog')
 *   @type int    $per_page         件数(既定: 4)
 *   @type bool   $include_children 子孫タームも含めるか(既定: false)
 *   @type array  $extra_query_args WP_Query へそのまま渡す追加引数(任意)
 * }
 * @return WP_Query|null  条件が作れない場合は null
 */
function mytheme_get_related_query_by_deepest_term( $args = array() ) {
	$defaults = array(
		'post_id'          => get_the_ID(),
		'taxonomy'         => 'category',
		'post_type'        => 'post',
		'per_page'         => 4,
		'include_children' => false,
		'extra_query_args' => array(),
	);
	$args = wp_parse_args( $args, $defaults );

	$post_id    = (int) $args['post_id'];
	$taxonomy   = $args['taxonomy'];
	$deepest_id = mytheme_get_deepest_term_id( $post_id, $taxonomy );

	if ( ! $deepest_id ) {
		return null; // タームが無い等
	}

	$q_args = array(
		'post_type'           => $args['post_type'],
		'posts_per_page'      => (int) $args['per_page'],
		'post__not_in'        => array( $post_id ),
		'ignore_sticky_posts' => true,
		'no_found_rows'       => true,
		'orderby'             => 'date',
		'order'               => 'DESC',
		'tax_query'           => array(
			array(
				'taxonomy'         => $taxonomy,
				'field'            => 'term_id',
				'terms'            => array( $deepest_id ),
				'include_children' => (bool) $args['include_children'],
			),
		),
	);

	// 追加の WP_Query 引数で上書き/拡張したい場合
	if ( ! empty( $args['extra_query_args'] ) && is_array( $args['extra_query_args'] ) ) {
		$q_args = array_merge( $q_args, $args['extra_query_args'] );
	}

	return new WP_Query( $q_args );
}

投稿ページ(single.phpなど)にサブループとして追加

通常投稿記事の場合

例1)通常投稿 × category

<?php
$q = mytheme_get_related_query_by_deepest_term();

if ( $q && $q->have_posts() ) :
  echo '<ul class="related-list">';
  while ( $q->have_posts() ) : $q->the_post();
    echo '<li><a href="' . esc_url( get_permalink() ) . '">';
    the_title();
    echo '</a></li>';
  endwhile;
  echo '</ul>';
  wp_reset_postdata();
endif;
?>

カスタム投稿タイプの場合

例2)カスタム投稿 ‘blog’ × タクソノミー ‘blog-cat’、子タームも含める、6件、ランダム

<?php
$q = mytheme_get_related_query_by_deepest_term( array(
  'post_type'        => 'blog',
  'taxonomy'         => 'blog-cat',
  'include_children' => true,
  'per_page'         => 6,
  'extra_query_args' => array(
    'orderby' => 'rand',
  ),
) );

if ( $q && $q->have_posts() ) :
  while ( $q->have_posts() ) : $q->the_post();
   // ここは自由にレイアウト
  endwhile;
  wp_reset_postdata();
endif;
?>

補足

  • 階層が同じカテゴリが複数ある場合は、最初に見つかったタームを採用
  • ターム名が見出しに必要なら、$term = get_term( mytheme_get_deepest_term_id( get_the_ID(), 'blog-cat' ), 'blog-cat' ); のように別途取得して表示

以下の環境で最終確認しています。
確認日:2025-08-16 / WordPress:6.8.2 / PHP:8.2.20

掲載しているコードや設定例は動作を保証するものではありません。
利用は自己責任で行い、必ずテスト環境での確認を行ってください。