【WordPress】プラグインなしで関連記事を表示する方法|transientキャッシュでorderby=randの負荷を軽減

WordPressサイトで関連記事を表示することで、ユーザーが興味を持つ他のコンテンツに簡単にアクセスでき、サイトの滞在時間を増やすことができます。この記事では、プラグインを使用せずにテーマファイルにコードを追加して関連記事を表示する方法を、負荷を抑える工夫とあわせて紹介します。

この記事でわかること

  • 同じカテゴリの投稿からランダムに関連記事を取得する方法
  • orderby => 'rand'が抱える負荷の問題とtransientキャッシュでの対策
  • DBに負荷をかけないPHP側shuffle()による代替案
スポンサーリンク

関連記事を表示するテンプレートファイルの選定

まず、関連記事を表示したいテンプレートファイルを決定します。通常、個別投稿ページのテンプレートである single.phpcontent-single.php にコードを追加します。

関連記事を取得するカスタムクエリ

現在の投稿のカテゴリに基づいて、同じカテゴリの他の投稿をランダムに取得します。

single.php:同じカテゴリからランダムに関連記事を取得
$current_post_id = get_the_ID();
$categories       = wp_get_post_categories( $current_post_id );

if ( $categories ) {
    $args = array(
        'category__in'   => $categories,
        'post__not_in'   => array( $current_post_id ),
        'posts_per_page' => 5,
        'orderby'        => 'rand',
        'no_found_rows'  => true, // ページネーション用の件数計算を省略
    );

    $related_posts = new WP_Query( $args );

    if ( $related_posts->have_posts() ) {
        echo '<div class="related-posts">';
        echo '<h3>関連記事</h3><ul>';

        while ( $related_posts->have_posts() ) {
            $related_posts->the_post();
            ?>
            <li><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></li>
            <?php
        }

        echo '</ul></div>';
        wp_reset_postdata();
    }
}

orderby=randはなぜ重いのか、transientでキャッシュする

orderby=randはテーブルが大きくなるほど遅くなる
orderby => 'rand' はMySQLの ORDER BY RAND() に相当する処理を行います。これは全行に乱数を割り当ててソートしてから抽出するため、記事数が増えるほどフルテーブルスキャンと一時ソートの負荷が増大します。個別記事ページはアクセスのたびに描画されるため、記事数が多いサイトでは無視できないボトルネックになり得ます。対策として、取得結果を transient で一定時間キャッシュするのが手軽です。
single.php:transientでキャッシュして負荷を軽減
$current_post_id = get_the_ID();
$cache_key        = 'related_posts_' . $current_post_id;
$related_ids      = get_transient( $cache_key );

if ( false === $related_ids ) {
    $categories = wp_get_post_categories( $current_post_id );
    $related_ids = array();

    if ( $categories ) {
        $query = new WP_Query( array(
            'category__in'   => $categories,
            'post__not_in'   => array( $current_post_id ),
            'posts_per_page' => 5,
            'orderby'        => 'rand',
            'no_found_rows'  => true,
            'fields'         => 'ids', // IDだけ取得して軽量化
        ) );

        $related_ids = $query->posts;
    }

    set_transient( $cache_key, $related_ids, DAY_IN_SECONDS ); // 1日キャッシュ
}

if ( $related_ids ) {
    echo '<div class="related-posts"><h3>関連記事</h3><ul>';
    foreach ( $related_ids as $post_id ) {
        echo '<li><a href="' . esc_url( get_permalink( $post_id ) ) . '">'
            . esc_html( get_the_title( $post_id ) ) . '</a></li>';
    }
    echo '</ul></div>';
}
投稿を更新したらキャッシュを消す
save_postフックでdelete_transient('related_posts_' . $post_id)を呼ぶようにすると、記事のカテゴリを変更した際に古いキャッシュが残り続けるのを防げます。

(代替案)PHP側でshuffle()する方法

キャッシュを使わずに毎回の負荷を抑えたい場合は、DBには通常のインデックスが効く並び順(orderby => 'date'等)で少し多めに取得し、PHP側で shuffle() してからランダム表示する方法もあります。

single.php:DBには負荷をかけずPHP側でランダム化
$categories = wp_get_post_categories( get_the_ID() );

if ( $categories ) {
    $query = new WP_Query( array(
        'category__in'   => $categories,
        'post__not_in'   => array( get_the_ID() ),
        'posts_per_page' => 20, // 候補を多めに取得
        'orderby'        => 'date', // インデックスが効く通常の並び順
        'no_found_rows'  => true,
        'fields'         => 'ids',
    ) );

    $ids = $query->posts;
    shuffle( $ids ); // PHP側でランダム化
    $related_ids = array_slice( $ids, 0, 5 );
}

デザインのカスタマイズ

関連記事の表示をより魅力的にするために、CSSでスタイルをカスタマイズできます。

style.css:関連記事のスタイル
.related-posts {
  margin-top: 20px;
}
.related-posts h3 {
  font-size: 1.5em;
  margin-bottom: 10px;
}
.related-posts ul {
  list-style-type: none;
  padding: 0;
}
.related-posts ul li {
  margin-bottom: 5px;
}
.related-posts ul li a {
  text-decoration: none;
  color: #0073aa;
}
.related-posts ul li a:hover {
  text-decoration: underline;
}

実装の確認と調整

コードを追加したら、実際にサイト上で関連記事が正しく表示されるか確認します。表示する投稿の数や順序、キャッシュの有効期限など、必要に応じて調整を行いましょう。

よくある質問(FAQ)

Q同じカテゴリーの関連記事をWP_Queryで表示するコードは?
Awp_get_post_categories()で現在の記事のカテゴリーIDを取得し、WP_Queryのcategory__in=カテゴリーID配列、post__not_in=array(get_the_ID())で現在の記事を除外して関連記事を取得します。
Qタグが同じ記事を関連記事として表示するには?
Aget_the_tags()でタグIDの配列を取得し、WP_Queryのtag__in=タグID配列で絞り込みます。カテゴリーとタグを組み合わせたOR検索はtax_queryを使います。
Q関連記事の表示をキャッシュして負荷を下げるには?
Aset_transient()とget_transient()を使い、記事IDをキーにしてクエリ結果をキャッシュします。有効期限を1日などに設定し、記事更新時(save_postフック)にdelete_transient()でキャッシュを削除します。

まとめ

  • 基本:category__in + post__not_in + orderby=rand
  • 注意:orderby=randは記事数が増えると重くなる
  • 対策:transientキャッシュ、またはPHP側shuffle()での代替

プラグインを使わずに関連記事を表示できますが、サイトの記事数が増えることを見越して負荷対策もあわせて実装しておきましょう。