2026年5月20日

2026年5月20日

WordPressに関連記事を表示する方法

はじめに

WordPressで関連記事を表示するには、WP_Query を使って現在の記事と同じカテゴリーやタグの記事を取得します。プラグイン不要で実装でき、表示件数やデザインを自由にカスタマイズできます。

症状・原因

  • 記事ページに関連記事を表示したい
  • 同じカテゴリーの記事を自動で表示したい
  • 関連記事のデザインをカスタマイズしたい
  • プラグインなしで関連記事を実装したい

解決手順

ステップ1:カテゴリーで関連記事を取得する

// single.php — ループの後に追加
<?php
// 現在の記事のカテゴリーIDを取得
$categories = get_the_category();
$category_ids = array_column($categories, 'term_id');

if ($category_ids) {
    $related = new WP_Query([
        'category__in'   => $category_ids,   // 同じカテゴリー
        'post__not_in'   => [get_the_ID()],  // 現在の記事を除外
        'posts_per_page' => 4,
        'orderby'        => 'rand',           // ランダム表示
        'no_found_rows'  => true,             // ページネーション不要なので高速化
    ]);

    if ($related->have_posts()):
?>
<section class="related-posts">
    <h2 class="related-title">関連記事</h2>
    <div class="related-grid">
        <?php while ($related->have_posts()): $related->the_post(); ?>
            <article class="related-card">
                <a href="<?php the_permalink(); ?>" class="related-link">
                    <?php if (has_post_thumbnail()): ?>
                        <div class="related-thumb">
                            <?php the_post_thumbnail('card-16-9', ['loading' => 'lazy']); ?>
                        </div>
                    <?php endif; ?>
                    <div class="related-body">
                        <h3 class="related-card-title"><?php the_title(); ?></h3>
                        <time class="related-date"><?php echo get_the_date(); ?></time>
                    </div>
                </a>
            </article>
        <?php endwhile; ?>
    </div>
</section>
<?php
    endif;
    wp_reset_postdata(); // 必須:グローバルクエリを元に戻す
}
?>

ステップ2:タグも考慮した関連度スコアで取得する

// functions.php — 関連度の高い記事を取得する関数
function get_related_posts(int $post_id, int $limit = 4): WP_Query {
    $categories = get_the_category($post_id);
    $tags       = get_the_tags($post_id);

    $category_ids = $categories ? array_column($categories, 'term_id') : [];
    $tag_ids      = $tags       ? array_column($tags, 'term_id') : [];

    // タグ一致を優先し、カテゴリー一致を次に
    $args = [
        'post__not_in'   => [$post_id],
        'posts_per_page' => $limit,
        'orderby'        => 'relevance',
        'no_found_rows'  => true,
    ];

    if ($tag_ids) {
        $args['tag__in'] = $tag_ids;
    } elseif ($category_ids) {
        $args['category__in'] = $category_ids;
    }

    return new WP_Query($args);
}
// single.php で使用
$related = get_related_posts(get_the_ID());
if ($related->have_posts()) {
    // ループ処理
    wp_reset_postdata();
}

ステップ3:CSSでカードレイアウトを設定する

/* 関連記事グリッド */
.related-posts {
    margin-top: 48px;
    padding-top: 32px;
    border-top: 2px solid var(--border-color, #c3c4c7);
}

.related-title {
    font-size: 1.25rem;
    margin-bottom: 20px;
}

.related-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
    gap: 20px;
}

.related-card {
    border-radius: 8px;
    overflow: hidden;
    box-shadow: var(--shadow-sm, 0 1px 3px rgba(0,0,0,.12));
    transition: transform 0.2s, box-shadow 0.2s;
}

.related-card:hover {
    transform: translateY(-4px);
    box-shadow: var(--shadow-md, 0 4px 12px rgba(0,0,0,.15));
}

.related-link {
    display: block;
    text-decoration: none;
    color: inherit;
}

.related-thumb img {
    width: 100%;
    aspect-ratio: 16 / 9;
    object-fit: cover;
    display: block;
}

.related-body {
    padding: 12px;
}

.related-card-title {
    font-size: 0.9rem;
    line-height: 1.5;
    margin: 0 0 6px;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.related-date {
    font-size: 0.8rem;
    color: var(--text-muted, #50575e);
}

@media (max-width: 768px) {
    .related-grid {
        grid-template-columns: repeat(2, 1fr);
        gap: 12px;
    }
}

ステップ4:パフォーマンスを改善する

// functions.php — 関連記事をトランジェントでキャッシュ
function get_cached_related_posts(int $post_id, int $limit = 4): WP_Query {
    $cache_key = "related_posts_{$post_id}_{$limit}";
    $cached    = get_transient($cache_key);

    if ($cached !== false) {
        return $cached;
    }

    $related = get_related_posts($post_id, $limit);

    // 12時間キャッシュ
    set_transient($cache_key, $related, 12 * HOUR_IN_SECONDS);

    return $related;
}

// 記事更新時にキャッシュをクリア
add_action('save_post', function(int $post_id): void {
    delete_transient("related_posts_{$post_id}_4");
});

注意事項

  • WP_Query のループ後は必ず wp_reset_postdata() を呼び出してください。呼び忘れるとグローバルの $post が壊れ、後続の処理でバグが発生します
  • orderby => 'rand' はキャッシュが効かず、毎回DBクエリが発生します。アクセスが多いサイトではトランジェントキャッシュを組み合わせてください
  • no_found_rows => true はページネーション用のカウントクエリをスキップするので、関連記事のような用途では必ず指定してパフォーマンスを改善してください

まとめ

WP_Querycategory__inpost__not_inposts_per_page を指定して関連記事を取得し、ループ後に wp_reset_postdata() を呼び出します。トランジェントキャッシュを使うとDBへの負荷を大幅に削減できます。

お気軽にご相談ください

お見積りへ お問い合わせへ