※社内専用

投稿記事一覧の1つ目の記事をキャッシュでランダム表示

  • HOME
  • Education
  • 投稿記事一覧の1つ目の記事をキャッシュでランダム表示

EPRESSの投稿記事一覧の1つ目をキャッシュを用いてランダム表示する機能の実装を行ったので、共有。

下記が実装サイト
https://www.daiki-k.jp/voice

仕様

概要

投稿記事一覧のショートコードにて、1つ目の記事をランダム表示させる。

2記事目以降は、新着順に表示させる。

ランダム表示の条件

ブラウザごとで異なるランダムな記事が表示されるような単純なものではなく、どの環境でも同様のランダムな記事が表示される。

また、10日ごとに1つ目の記事を再度ランダム表示させる。

なお、1つ目にランダム表示された記事は、2記事目以降の新着順には含まれないように制御する。

上記を実現するために、単純にWP_Queryの設定の「’orderby’ => ‘rand’」にするだけではなく、WordPressのtransient APIの有限付きキャッシュ機能を組み合わせて、実装する。

実装手順

テーマフォルダ内のfunctions > customフォルダ内にショートコードファイルを追加。

※今回は、仮にshortcode-first_random.phpとする

shortcode-first_random.php

<?php
  add_shortcode( 'first_random', function($atts) {
    extract(shortcode_atts( array(
        'slug' => null,
        'per' => '10',
    ), $atts ));

    $paged = get_query_var('paged');
    
    // ランダムに表示される記事の取得 (10日ごとに変更)
    $first_random_post_id = get_transient( 'first_random_post_id' );
    
    // ランダム記事が設定されていないか、10日経過している場合は新しい記事を取得
    if ( !$first_random_post_id || time() - get_transient( 'first_random_post_timestamp' ) > 864000 ) {
      // ランダムに記事を選択
      $args = array(
        'posts_per_page' => 1,
        'category_name'  => $slug,
        'orderby'        => 'rand',
        'post_type'      => 'post',
      );
      $random_query = new WP_Query( $args );
      if ( $random_query->have_posts() ) {
        $random_query->the_post();
        $first_random_post_id = get_the_ID();
        // ランダム記事をtransientに保存
        set_transient( 'first_random_post_id', $first_random_post_id, 864000 ); // 10日間有効
        set_transient( 'first_random_post_timestamp', time(), 864000 ); // タイムスタンプも保存
        }
        wp_reset_postdata();
      }
      
      // 新着順で記事を取得(ランダム記事は除外)
      $args = array(
        'posts_per_page' => $per - 1, // ランダム記事1件を除く
        'paged' => $paged,
        'category_name' => $slug,
        'post_type' => 'post',
        'post__not_in' => array( $first_random_post_id ), // ランダム記事を除外
      );
      $the_query = new WP_Query( $args );
      
      // 出力処理
      if ($the_query->have_posts()) {
      ob_start();
      ?>
      <dl>
        <?php
        // 先頭にランダム記事を表示
        if ( $first_random_post_id ):
          $random_post = get_post( $first_random_post_id );
          $subtitle = get_field("subtitle", $first_random_post_id);
        ?>
        <dt><p><?= get_the_time('Y/m/d') ?></p></dt>
        <dd><p><a href="<?= get_the_permalink() ?>"><?= get_the_title() ?></a></p></dd>
        <?php
        endif;
        
        // 新着順の記事を表示
        while ( $the_query->have_posts() ) : $the_query->the_post();
        $subtitle = get_field("subtitle");
        ?>
        <dt><p><?= get_the_time('Y/m/d') ?></p></dt>
        <dd><p><a href="<?= get_the_permalink() ?>"><?= get_the_title() ?></a></p></dd>
        <?php endwhile; wp_reset_postdata(); ?>
      </dl>
    <?php
    return ob_get_clean();
    }
  });

コードの解説

$first_random_post_id = get_transient( 'first_random_post_id' );
if ( !$first_random_post_id || time() - get_transient( 'first_random_post_timestamp' ) > 864000 ) {

上記二つのコードで、WordPressのtransient APIの有限付きキャッシュ機能を用いて、キャッシュの設定と有効期限の設定、および期限付きキャッシュの条件分岐を書いている。

transient APIは、 WordPressで使える簡易なキャッシュ機能で、有効期限付きでデータをDB(wp_optionsテーブル)に一時保存できる。

このtransient APIを用いて、キャッシュの値を取得し、条件分岐にて、取得したキャッシュの値が存在しない場合(まだ設定されていない場合や期限が切れた場合)もしくは、キャッシュの値は設定されているが、それが10日以上前の値の場合に、1記事目のランダム表示を再度実行している。

      // ランダムに記事を選択
      $args = array(
        'posts_per_page' => 1,
        'category_name'  => $slug,
        'orderby'        => 'rand',
        'post_type'      => 'post',
      );
      $random_query = new WP_Query( $args );
      if ( $random_query->have_posts() ) {
        $random_query->the_post();
        $first_random_post_id = get_the_ID();
        // ランダム記事をtransientに保存
        set_transient( 'first_random_post_id', $first_random_post_id, 864000 ); // 10日間有効
        set_transient( 'first_random_post_timestamp', time(), 864000 ); // タイムスタンプも保存
        }
        wp_reset_postdata();
      }

上記が、前述の条件分岐の中身で、orderbyをrandにして、記事を一つだけランダム取得している。

取得した記事はtransient APIに保存している。

この保存した値がデータが、10日間有効なデータとして記録される

タイムスタンプも同時に保存する。

      // 新着順で記事を取得(ランダム記事は除外)
      $args = array(
        'posts_per_page' => $per - 1, // ランダム記事1件を除く
        'paged' => $paged,
        'category_name' => $slug,
        'post_type' => 'post',
        'post__not_in' => array( $first_random_post_id ), // ランダム記事を除外
      );
      $the_query = new WP_Query( $args );

上記は、2記事目以降の新着順の記事を取得するコード。

posts_per_pageを$per – 1にすることで、ランダム記事1件分、記事を減らしており、post__not_inにarray( $first_random_post_id )を設定することで、ランダム記事を除外している。

これで、1記事目にランダム表示された記事は、2記事目以降の新着記事からは除外される(記事のだぶりを回避できる)

      // 出力処理
      if ($the_query->have_posts()) {
      ob_start();
      ?>
      <dl>
        <?php
        // 先頭にランダム記事を表示
        if ( $first_random_post_id ):
          $random_post = get_post( $first_random_post_id );
          $subtitle = get_field("subtitle", $first_random_post_id);
        ?>
        <dt><p><?= get_the_time('Y/m/d') ?></p></dt>
        <dd><p><a href="<?= get_the_permalink() ?>"><?= get_the_title() ?></a></p></dd>
        <?php
        endif;
        
        // 新着順の記事を表示
        while ( $the_query->have_posts() ) : $the_query->the_post();
        $subtitle = get_field("subtitle");
        ?>
        <dt><p><?= get_the_time('Y/m/d') ?></p></dt>
        <dd><p><a href="<?= get_the_permalink() ?>"><?= get_the_title() ?></a></p></dd>
        <?php endwhile; wp_reset_postdata(); ?>
      </dl>
    <?php
    return ob_get_clean();
    }

最後に、上記コードで、出力処理をしている。

その際に、先頭にランダム記事を出力し、そのあとに新着順の記事を出力するようにしている。