2026年5月29日
2026年5月29日
WordPressに関連記事を表示する方法
はじめに
「投稿の下に関連記事を表示してユーザーの回遊率を上げたい」「プラグインを使わずに軽量な関連記事を実装したい」「タグとカテゴリー両方で関連度を判定したい」——関連記事はユーザーの滞在時間と内部リンクSEOに直結する重要な要素です。
症状・原因
デフォルトのWordPressには関連記事機能がないため、WP_Queryやget_posts()を使って同一タグ・カテゴリーの投稿を取得する必要があります。プラグインは手軽ですが重くなりがちで、自前実装なら表示ロジックとデザインを完全制御できます。
解決手順
ステップ1:タグベースの関連記事を取得する
// functions.php: タグベースの関連記事取得関数
function get_related_posts( $post_id = null, $count = 4 ) {
$post_id = $post_id ?: get_the_ID();
// 現在の投稿のタグを取得
$tags = wp_get_post_tags( $post_id, [ 'fields' => 'ids' ] );
if ( empty( $tags ) ) {
// タグがない場合はカテゴリーでフォールバック
$cats = wp_get_post_categories( $post_id );
if ( empty( $cats ) ) {
return [];
}
$tax_query = [ [
'taxonomy' => 'category',
'field' => 'term_id',
'terms' => $cats,
] ];
} else {
$tax_query = [ [
'taxonomy' => 'post_tag',
'field' => 'term_id',
'terms' => $tags,
] ];
}
$related = get_posts( [
'post__not_in' => [ $post_id ], // 現在の投稿を除外
'posts_per_page' => $count,
'orderby' => 'rand', // ランダム順(キャッシュ利用時は date 推奨)
'tax_query' => $tax_query,
'post_status' => 'publish',
'no_found_rows' => true, // ページネーション不要なのでカウント省略
] );
return $related;
}
ステップ2:タグ+カテゴリーで関連度スコアを付けて取得する
// functions.php: スコアベースの関連記事(より精度が高い)
function get_scored_related_posts( $post_id = null, $count = 4 ) {
$post_id = $post_id ?: get_the_ID();
$tags = wp_get_post_tags( $post_id, [ 'fields' => 'ids' ] );
$cats = wp_get_post_categories( $post_id );
$tax_query = [ 'relation' => 'OR' ];
if ( ! empty( $tags ) ) {
$tax_query[] = [
'taxonomy' => 'post_tag',
'field' => 'term_id',
'terms' => $tags,
];
}
if ( ! empty( $cats ) ) {
$tax_query[] = [
'taxonomy' => 'category',
'field' => 'term_id',
'terms' => $cats,
];
}
if ( count( $tax_query ) === 1 ) {
return []; // タグもカテゴリーもない場合
}
$posts = get_posts( [
'post__not_in' => [ $post_id ],
'posts_per_page' => $count * 3, // 多めに取得してスコアでフィルタ
'orderby' => 'date',
'tax_query' => $tax_query,
'post_status' => 'publish',
'no_found_rows' => true,
] );
// スコアリング:共通タグ数 × 2 + 共通カテゴリー数
$scored = [];
foreach ( $posts as $post ) {
$post_tags = wp_get_post_tags( $post->ID, [ 'fields' => 'ids' ] );
$post_cats = wp_get_post_categories( $post->ID );
$tag_overlap = count( array_intersect( $tags, $post_tags ) );
$cat_overlap = count( array_intersect( $cats, $post_cats ) );
$score = $tag_overlap * 2 + $cat_overlap;
if ( $score > 0 ) {
$scored[] = [ 'post' => $post, 'score' => $score ];
}
}
// スコア順にソート
usort( $scored, fn( $a, $b ) => $b['score'] - $a['score'] );
return array_column( array_slice( $scored, 0, $count ), 'post' );
}
ステップ3:関連記事をテンプレートに表示する
<?php
// single.php: 関連記事セクション
$related_posts = get_related_posts( get_the_ID(), 4 );
if ( ! empty( $related_posts ) ) : ?>
<section class="related-posts" aria-label="関連記事">
<h2 class="related-posts__title">関連記事</h2>
<div class="related-posts__grid">
<?php foreach ( $related_posts as $related ) : ?>
<article class="related-card">
<?php if ( has_post_thumbnail( $related->ID ) ) : ?>
<a class="related-card__thumb" href="<?php echo esc_url( get_permalink( $related->ID ) ); ?>">
<?php echo get_the_post_thumbnail( $related->ID, 'medium', [
'loading' => 'lazy',
'alt' => esc_attr( get_the_title( $related->ID ) ),
] ); ?>
</a>
<?php endif; ?>
<div class="related-card__body">
<?php
$cats = get_the_category( $related->ID );
if ( $cats ) : ?>
<a class="related-card__cat" href="<?php echo esc_url( get_category_link( $cats[0]->term_id ) ); ?>">
<?php echo esc_html( $cats[0]->name ); ?>
</a>
<?php endif; ?>
<h3 class="related-card__title">
<a href="<?php echo esc_url( get_permalink( $related->ID ) ); ?>">
<?php echo esc_html( get_the_title( $related->ID ) ); ?>
</a>
</h3>
<time class="related-card__date" datetime="<?php echo esc_attr( get_the_date( 'c', $related->ID ) ); ?>">
<?php echo esc_html( get_the_date( '', $related->ID ) ); ?>
</time>
</div>
</article>
<?php endforeach; ?>
</div>
</section>
<?php endif; ?>
ステップ4:トランジェントキャッシュで高速化する
// functions.php: キャッシュ付き関連記事取得
function get_related_posts_cached( $post_id = null, $count = 4 ) {
$post_id = $post_id ?: get_the_ID();
$cache_key = 'related_posts_' . $post_id . '_' . $count;
$cached = get_transient( $cache_key );
if ( false !== $cached ) {
return $cached;
}
$posts = get_related_posts( $post_id, $count );
set_transient( $cache_key, $posts, 12 * HOUR_IN_SECONDS );
return $posts;
}
// 投稿が更新されたらキャッシュをクリア
add_action( 'save_post', function( $post_id ) {
if ( wp_is_post_autosave( $post_id ) || wp_is_post_revision( $post_id ) ) {
return;
}
$count = 4;
delete_transient( 'related_posts_' . $post_id . '_' . $count );
} );
ステップ5:CSSでカード型グリッドレイアウトを実装する
/* 関連記事セクション */
.related-posts {
margin: 3rem 0;
padding-top: 2rem;
border-top: 2px solid #dcdcde;
}
.related-posts__title {
font-size: 1.25rem;
font-weight: 700;
margin: 0 0 1.5rem;
color: #1d2327;
}
.related-posts__grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 1.25rem;
}
@media (max-width: 1024px) {
.related-posts__grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 600px) {
.related-posts__grid { grid-template-columns: 1fr; }
}
/* 関連記事カード */
.related-card {
background: #fff;
border-radius: 6px;
overflow: hidden;
box-shadow: 0 1px 4px rgba(0,0,0,0.08);
transition: transform 0.2s, box-shadow 0.2s;
}
.related-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0,0,0,0.12);
}
.related-card__thumb img {
width: 100%;
aspect-ratio: 16 / 9;
object-fit: cover;
display: block;
}
.related-card__body {
padding: 0.875rem;
}
.related-card__cat {
display: inline-block;
font-size: 0.7rem;
font-weight: 600;
color: #2271b1;
text-decoration: none;
text-transform: uppercase;
letter-spacing: 0.05em;
margin-bottom: 0.4rem;
}
.related-card__title {
font-size: 0.9rem;
font-weight: 600;
line-height: 1.5;
margin: 0 0 0.5rem;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.related-card__title a {
color: #1d2327;
text-decoration: none;
}
.related-card__title a:hover {
color: #2271b1;
}
.related-card__date {
font-size: 0.75rem;
color: #a7aaad;
}
注意事項
orderby => 'rand'はページが表示されるたびに異なる結果を返すため、キャッシュと組み合わせる場合はorderby => 'date'やorderby => 'comment_count'など安定した順序に変更してください。no_found_rows => trueを設定するとSQL_CALC_FOUND_ROWSが省略され、件数が不要な場面でのクエリが高速化されます。- 関連記事が多いサイトでは
Jetpackの関連記事機能やYet Another Related Posts Plugin (YARPP)も選択肢ですが、自前実装の方がページスピードへの影響が少なく、デザインも自由に制御できます。
まとめ
関連記事の実装は「get_posts()でタグ・カテゴリーを条件にした関連投稿を取得→スコアリングで関連度を計算→single.phpにカード型グリッドで表示→トランジェントキャッシュで高速化→CSSのgrid + hover効果で仕上げ」の流れで整備します。関連記事:WordPressの抜粋文(excerpt)をカスタマイズする方法、WordPressにソーシャルシェアボタンを設置する方法。