「この記事を読んだ人はこちらも読んでいます」リンクを自動生成してサイト内回遊率をアップする方法

WordPressの投稿本文を読み終わったところに、同じカテゴリーの記事を自動表示すると、読者がサイト内の別記事を見つけやすくなります。この記事では、通常の投稿ページだけを対象に、現在の記事と同じカテゴリーに属する別の記事を3件ランダムで取得し、本文下へ「こちらの記事もおすすめです」と表示する方法を紹介します。

固定ページやアーカイブページ、カスタム投稿タイプには表示せず、WordPressのメインループ内にある通常投稿の本文だけへ追加します。プラグインを使わず、子テーマの functions.php とCSSで実装できます。

1 今回実装する機能

  • 通常の投稿ページだけに関連記事を表示
  • 現在の記事が属するカテゴリーを利用
  • 複数カテゴリーが設定されている投稿にも対応
  • 現在表示している記事は関連記事から除外
  • 同じカテゴリーの記事からランダムで3件表示
  • 固定ページ・アーカイブ・カスタム投稿タイプには表示しない
  • メインループ以外の本文には追加しない

2 functions.phpにコードを追加

子テーマの functions.php に次のコードを追加します。親テーマへ直接追加すると、テーマ更新時に変更内容が消える可能性があるため、子テーマを使用してください。

/**
 * 通常投稿の本文下に
 * 同じカテゴリーの記事を3件表示する
 */
add_filter( 'the_content', 'wpt_add_related_category_posts' );

function wpt_add_related_category_posts( $content ) {

	/*
	 * 通常投稿の個別ページ・メインループだけを対象にする。
	 */
	if (
		! is_singular( 'post' )
		|| ! in_the_loop()
		|| ! is_main_query()
	) {
		return $content;
	}

	$post_id = get_the_ID();

	if ( ! $post_id ) {
		return $content;
	}

	/*
	 * 現在の記事が属するカテゴリーIDを取得。
	 */
	$category_ids = wp_get_post_categories( $post_id );

	if ( empty( $category_ids ) ) {
		return $content;
	}

	/*
	 * 同じカテゴリーの記事を取得。
	 */
	$related_posts = get_posts(
		array(
			'post_type'      => 'post',
			'post_status'    => 'publish',
			'posts_per_page' => 3,
			'post__not_in'   => array( $post_id ),
			'category__in'   => $category_ids,
			'orderby'        => 'rand',
		)
	);

	if ( empty( $related_posts ) ) {
		return $content;
	}

	ob_start();
	?>

	<aside class="related-category-box" aria-labelledby="related-category-title">

		<h3 id="related-category-title">
			こちらの記事もおすすめです
		</h3>

		<ul>

			<?php foreach ( $related_posts as $related_post ) : ?>

				<li>
					<a href="<?php echo esc_url( get_permalink( $related_post->ID ) ); ?>">
						<?php echo esc_html( get_the_title( $related_post->ID ) ); ?>
					</a>
				</li>

			<?php endforeach; ?>

		</ul>

	</aside>

	<?php

	$related_html = ob_get_clean();

	return $content . $related_html;
}

3 このコードのポイント

今回のコードでは、単純に is_single() だけで判定していません。通常投稿だけに限定するため、次の条件を使用しています。

is_singular( 'post' )

is_single() は通常投稿だけでなく、固定ページ以外のカスタム投稿タイプの個別ページでもtrueになる場合があります。is_singular( 'post' ) とすることで、投稿タイプ post の個別ページだけに限定できます。

4 メインループだけに限定する

the_content フィルターは、テーマやプラグインの構成によってはメイン本文以外でも呼び出されることがあります。そのため、今回のコードでは次の2つも確認しています。

in_the_loop()
is_main_query()

これによって、サイドバーや別の投稿ループなどへ意図せず関連記事ブロックが追加される可能性を減らしています。

5 複数カテゴリーに対応する

現在の記事が属するカテゴリーは、次のコードで取得しています。

$category_ids = wp_get_post_categories( $post_id );

たとえば現在の記事に、次の3カテゴリーが設定されているとします。

  • WordPress
  • CSS
  • Lightning

この場合は3つのカテゴリーIDを取得し、そのいずれかに属する投稿を関連記事候補として取得します。

元のコードのように、

$categories[0]->term_id

だけを使用すると、複数カテゴリーが設定されていても最初に取得した1カテゴリーだけが対象になります。今回のコードでは、現在の記事が属するカテゴリー全体を対象にしています。

6 現在の記事は関連記事から除外する

現在表示している記事そのものが関連記事へ表示されないように、次の指定を入れています。

'post__not_in' => array( $post_id ),

これによって、閲覧中の記事を除外したうえで別の記事を取得できます。

7 表示件数を変更する方法

関連記事は現在3件に設定しています。

'posts_per_page' => 3,

5件表示したい場合は、次のように変更します。

'posts_per_page' => 5,

8 ランダム表示の仕組み

今回のコードでは、同じカテゴリーの記事を毎回同じ順番ではなくランダムに表示するため、次の指定を使用しています。

'orderby' => 'rand',

これによってアクセスするたびに異なる記事が選ばれる場合があります。ただし、ランダム取得は記事数やアクセス数が非常に多いサイトではデータベースへの負荷が大きくなる場合があります。

小規模から中規模のWordPressサイトで3件程度を表示する簡易的な関連記事としては使いやすい方法ですが、大規模サイトではランダムではなく新着順などを検討してもよいでしょう。

9 新しい関連記事を優先する場合

ランダムではなく、同じカテゴリーの新しい記事を3件表示したい場合は、

'orderby' => 'rand',

を次のように変更します。

'orderby' => 'date',
'order'   => 'DESC',

これで同じカテゴリーの記事から、公開日の新しい順に表示されます。

10 古い記事を優先する場合

逆に古い記事から表示したい場合は、

'orderby' => 'date',
'order'   => 'ASC',

とします。

11 「この記事を読んだ人はこちらも読んでいます」としない理由

今回のコードは、読者の閲覧履歴や他ユーザーの行動データを解析して関連記事を選んでいるわけではありません。単純に現在の記事と同じカテゴリーに属する記事を取得しています。

そのため、

この記事を読んだ人はこちらも読んでいます

という見出しよりも、

こちらの記事もおすすめです

または、

同じカテゴリーの記事

などの表現の方が実際の仕組みに合っています。

12 「お知らせ」をカスタム投稿タイプで作っている場合

今回のコードは、

is_singular( 'post' )

で通常投稿だけに限定しています。そのため、たとえば newsinfo などのカスタム投稿タイプで作成した「お知らせ」の個別ページには関連記事ブロックは表示されません。

13 「お知らせ」を通常投稿のカテゴリーで管理している場合

一方、「お知らせ」をカスタム投稿タイプではなく通常投稿のカテゴリーとして作成している場合は、その記事も投稿タイプ post なので今回の対象になります。

たとえば「お知らせ」カテゴリーのスラッグが info で、そのカテゴリーだけ関連記事を表示したくない場合は、条件部分へ次の指定を追加できます。

if (
	! is_singular( 'post' )
	|| ! in_the_loop()
	|| ! is_main_query()
	|| has_category( 'info' )
) {
	return $content;
}

info の部分は、実際のカテゴリーのスラッグに変更してください。

14 カテゴリーを指定して除外する方法

複数のカテゴリーを除外したい場合は、次のように指定できます。

if (
	has_category(
		array(
			'info',
			'news',
		'notice',
		)
	)
) {
	return $content;
}

このようにして、サイト構成に合わせて対象カテゴリーを調整できます。

15 出力時にエスケープする

関連記事のURLは、次のように esc_url() を使って出力しています。

<?php echo esc_url( get_permalink( $related_post->ID ) ); ?>

タイトルには esc_html() を使用しています。

<?php echo esc_html( get_the_title( $related_post->ID ) ); ?>

WordPressでHTMLへデータを出力する場合は、データの用途に合わせて適切にエスケープして出力することが大切です。

16 関連記事ブロックのCSS

次のCSSを子テーマの style.css などへ追加します。

/* =========================================================
	同じカテゴリーの関連記事
========================================================= */

.related-category-box {
	margin: 40px 0;
	padding: 20px;
	border-left: 5px solid #0073aa;
	border-radius: 8px;
	background: #f9f9ff;
}

.related-category-box h3 {
	margin: 0 0 15px;
	padding: 0;
	border: 0;
	background: none;
	color: #0073aa;
	font-size: 1.2rem;
	font-weight: 700;
}

.related-category-box ul {
	margin: 0;
	padding: 0;
	list-style: none;
}

.related-category-box li {
	position: relative;
	margin: 0;
	padding: 8px 0 8px 1.2em;
	border-bottom: 1px dashed #ccc;
	line-height: 1.7;
}

.related-category-box li:last-child {
	border-bottom: 0;
}

.related-category-box li::before {
	content: "・";
	position: absolute;
	top: 8px;
	left: 0;
	color: #0073aa;
	font-weight: 700;
}

.related-category-box li a {
	color: #0073aa;
	text-decoration: none;
}

.related-category-box li a:hover {
	text-decoration: underline;
}

.related-category-box li a:focus-visible {
	outline: 2px solid currentColor;
	outline-offset: 3px;
}

17 元のCSSからtext-indentを使わない形に変更

行頭の「・」を表示するために、

text-indent: -1em;

padding-left を組み合わせる方法もありますが、今回のCSSでは疑似要素を絶対配置しているため、text-indent は使用していません。

.related-category-box li {
	position: relative;
	padding-left: 1.2em;
}

.related-category-box li::before {
	content: "・";
	position: absolute;
	left: 0;
}

この方が、関連記事タイトルが2行以上になった場合でも行頭をそろえやすくなります。

18 Lightningなどのテーマでh3装飾が付く場合

テーマによっては h3 に下線や背景色、余白などが自動的に付く場合があります。今回のCSSでは、関連記事ブロック内の見出しだけに限定して、次のように一部をリセットしています。

.related-category-box h3 {
	margin: 0 0 15px;
	padding: 0;
	border: 0;
	background: none;
}

テーマ全体の h3 を変更するのではなく、関連記事ブロック内だけを対象にすることで他ページへの影響を減らしています。

19 サムネイル画像も表示したい場合

今回の記事ではシンプルなテキストリンクだけにしていますが、必要であればアイキャッチ画像も表示できます。ループ部分を次のように変更します。

<li>

	<a href="<?php echo esc_url( get_permalink( $related_post->ID ) ); ?>">

		<?php
		echo get_the_post_thumbnail(
			$related_post->ID,
			'thumbnail',
			array(
				'class' => 'related-category-thumb',
				'alt'   => '',
			)
		);
		?>

		<span>
			<?php echo esc_html( get_the_title( $related_post->ID ) ); ?>
		</span>

	</a>

</li>

ただし、画像を追加するとデザインやスマートフォン対応も必要になるため、記事タイトルだけの表示で十分な場合は現在のシンプルな構成の方が管理しやすくなります。

20 関連記事が表示されない場合

関連記事ブロックが表示されない場合は、次の項目を確認してください。

  • 現在のページが通常投稿の個別ページか
  • 現在の記事にカテゴリーが設定されているか
  • 同じカテゴリーに別の記事が存在するか
  • 別の記事が公開状態になっているか
  • 除外カテゴリーに該当していないか
  • 子テーマのfunctions.phpにコードが正しく追加されているか

現在の記事しか存在しないカテゴリーでは、表示する関連記事がないためブロック自体を出力しません。

21 関連記事をランダム表示する場合の注意

orderby => rand はWordPressで利用できる方法ですが、データベースからランダムに記事を選ぶ処理は、単純な新着順取得などと比較すると負荷が高くなる場合があります。

投稿数が少ない一般的なブログで数件表示する程度なら使いやすい方法ですが、記事数が非常に多いサイトやアクセス数が多いサイトでは、新着順にする、結果をキャッシュする、関連記事専用プラグインを利用するなどの方法も検討してください。

22 PHPだけだから必ず軽いとは限らない

プラグインを使わずPHPコードだけで実装すると構成はシンプルになりますが、それだけで必ずプラグインより高速になるわけではありません。特にランダム検索はアクセスのたびに関連記事を取得するため、サイト規模によっては専用プラグインのキャッシュ機能を利用した方が効率的な場合もあります。

小規模なサイトで「同じカテゴリーから3件表示する」という単純な機能だけが必要なら、今回の方法は比較的シンプルに導入できます。

23 関連記事プラグインを利用する方法

カテゴリーだけではなく、タイトル・本文・タグなども含めて関連記事の関連度を判定したい場合は、関連記事専用プラグインを利用する方法もあります。

  • Contextual Related Posts:投稿内容などを利用して関連記事を表示できる
  • Yet Another Related Posts Plugin(YARPP):タイトル・本文・カテゴリー・タグなどを利用した関連記事表示に対応

プラグインは機能が豊富ですが、WordPress本体やPHPのバージョンとの互換性、最終更新日、現在のサポート状況を確認してから導入してください。

24 今回の完成コード

通常投稿だけに、同じカテゴリーの記事をランダムで3件表示する基本コードは次のとおりです。

/**
 * 通常投稿の本文下に
 * 同じカテゴリーの記事を3件表示する
 */
add_filter( 'the_content', 'wpt_add_related_category_posts' );

function wpt_add_related_category_posts( $content ) {

	if (
		! is_singular( 'post' )
		|| ! in_the_loop()
		|| ! is_main_query()
	) {
		return $content;
	}

	$post_id = get_the_ID();

	if ( ! $post_id ) {
		return $content;
	}

	$category_ids = wp_get_post_categories( $post_id );

	if ( empty( $category_ids ) ) {
		return $content;
	}

	$related_posts = get_posts(
		array(
			'post_type'      => 'post',
			'post_status'    => 'publish',
			'posts_per_page' => 3,
			'post__not_in'   => array( $post_id ),
			'category__in'   => $category_ids,
			'orderby'        => 'rand',
		)
	);

	if ( empty( $related_posts ) ) {
		return $content;
	}

	ob_start();
	?>

	<aside class="related-category-box" aria-labelledby="related-category-title">

		<h3 id="related-category-title">
			こちらの記事もおすすめです
		</h3>

		<ul>

			<?php foreach ( $related_posts as $related_post ) : ?>

				<li>
					<a href="<?php echo esc_url( get_permalink( $related_post->ID ) ); ?>">
						<?php echo esc_html( get_the_title( $related_post->ID ) ); ?>
					</a>
				</li>

			<?php endforeach; ?>

		</ul>

	</aside>

	<?php

	return $content . ob_get_clean();
}

25 まとめ

WordPressの投稿本文下へ同じカテゴリーの記事を自動表示すると、読者がサイト内の別記事を見つけるための導線を追加できます。今回のコードでは、通常投稿のメイン本文だけを対象にし、現在の記事を除外したうえで、現在の記事が属するカテゴリーから関連記事を3件取得しています。

  • is_singular( 'post' ) で通常投稿だけに限定する
  • in_the_loop()is_main_query() でメイン本文だけを対象にする
  • 現在の記事が属する複数カテゴリーに対応する
  • post__not_in で現在の記事を除外する
  • URLは esc_url()、タイトルは esc_html() で出力する
  • ランダム表示は便利だが、大規模サイトでは負荷も考慮する
  • 「この記事を読んだ人」ではなく、実際の仕組みに合った見出しを使う

今回の方法は「同じカテゴリーの記事を簡単に表示したい」という用途に向いた実装です。 記事本文やタグなどまで分析して本当に内容の近い関連記事を選びたい場合は、関連記事専用プラグインや独自の関連度判定を利用する方法も検討してください。