「閲覧履歴」機能を追加する方法

Cocoonカスタマイズ集
この記事は約11分で読めます。
記事内に広告が含まれています。

はじめに

「新着記事」や「新着情報」に、ユーザーが閲覧した投稿ページを表示できる機能を追加します。

この記事では、この方法について説明します。

使用方法

actionオプションを追加します。

  • 新着記事
[new_list action="history"]
  • 新着情報
[info_list action="history"]

実装手順

以下のコードをfunctions.phpに追加します。

// クッキーに保存
add_action('template_redirect', function () {
  // historyパラメータが指定されている場合は保存対象外
  if (isset($_GET['history'])) {
    return;
  }

  // 投稿ページ以外は保存対象外
  if (!is_single()) {
    return;
  }

  $current_post_id = get_the_ID();

  if (!$current_post_id) {
    return;
  }

  // 最大履歴数を5件に設定
  $his_max = 5;

  // クッキーの有効期限を1年後に設定
  $uptime = time() + YEAR_IN_SECONDS;

  $histories = [];
  $set_histories = [];

  // クッキーを取得
  if (isset($_COOKIE['my_post_history'])) {
    $histories = json_decode(stripslashes($_COOKIE['my_post_history']), true);

    // 既存のクッキーの有効期限を更新
    setcookie('my_post_history', $_COOKIE['my_post_history'], [
      'expires'  => $uptime,
      'path'     => '/',
      'secure'   => is_ssl(),
      'httponly' => false,
      'samesite' => 'Lax',
    ]);
  }

  if (!is_array($histories)) {
    $histories = [];
  }

  // 最新の履歴が現在のページと同じ場合は終了
  if (!empty($histories) && $histories[0] == $current_post_id) {
    return;
  }

  // 新しい投稿IDを履歴の先頭に追加
  array_unshift($histories, $current_post_id);

  // 重複する投稿IDを削除してインデックスを整理
  $histories = array_values(array_unique($histories));

  // 最新の5件に制限
  $set_histories = array_slice($histories, 0, $his_max);

  $json_value = json_encode($set_histories);

  // 最新の履歴をクッキーに保存
  setcookie('my_post_history', $json_value, [
    'expires'  => $uptime,
    'path'     => '/',
    'secure'   => is_ssl(),
    'httponly' => false,
    'samesite' => 'Lax',
  ]);

  // $_COOKIEを更新して現在のリクエストに即時反映
  $_COOKIE['my_post_history'] = $json_value;
});


/**
 * 履歴投稿に基づいてウィジェットのクエリ引数を変更
 *
 * @param array $args 現在のウィジェットのクエリ引数
 * @return array 更新されたウィジェットのクエリ引数
 */
if (!function_exists('modify_widget_args')):
function modify_widget_args($args) {
  $is_history = !empty($GLOBALS['history']) || (isset($args['action']) && $args['action'] === 'history');

  if (!$is_history) {
    return $args;
  }

  $ids = [];

  if (isset($_COOKIE['my_post_history'])) {
    $ids = json_decode(stripslashes($_COOKIE['my_post_history']), true);
  }

  if (empty($ids) || !is_array($ids)) {
    $args['post__in'] = [0];
    $args['posts_per_page'] = 0;
  } else {
    $args['post__in'] = array_map('absint', $ids);
    $args['orderby'] = 'post__in';
  }

  $args['ignore_sticky_posts'] = true;

  return $args;
}
endif;


// 新着記事のクエリを設定
add_filter('widget_entries_args', 'modify_widget_args');


// 新着情報のクエリを設定
add_filter('get_info_list_args', 'modify_widget_args');

解説

  • 投稿ページを閲覧した際、そのページ情報はクッキーに登録。
    ただし、リロードしないと一覧表示には反映されないため、クッキーの情報をセッションにも反映させることでリアルタイムで反映。
  • 閲覧した投稿ページはクッキーに登録され、最大で5件まで保存。
    そのため、ウィジェットの表示数を6件以上に設定しても、最大で5件しか表示されない。
  • クッキーの保存期間は1年に設定。

拡張:ウィジェットにオプションを追加

ショートコードのみを使用し、ウィジェットを使用しない場合、以下のコードは不要です。

ウィジェット

「新着記事」ウィジェットや「新着情報」ウィジェットの設定画面に「履歴」メニューを追加します(図1参照)。

実装手順

以下のコードをfunctions.phpに追加します。

// ウィジェットに設定メニューを追加
add_action('in_widget_form', function ($widget, $return, $instance) {
  // 対象外は終了
  if (!in_array($widget->id_base, ['new_entries', 'info_list'], true)) {
    return;
  }

  $f_id    = $widget->get_field_id('history');
  $f_name  = $widget->get_field_name('history');
  $checked = checked(!empty($instance['history']), true, false);

  ?>
  <p>
    <input type="checkbox" id="<?= esc_attr($f_id); ?>" name="<?= esc_attr($f_name); ?>" value="1" <?= $checked; ?>>
    <label for="<?= esc_attr($f_id); ?>">履歴</label>
  </p>
  <?php
}, 10, 3);


// ウィジェットの入力データを更新
add_filter('widget_update_callback', function ($instance, $new_instance, $old_instance, $widget) {
  // 対象外は終了
  if (!in_array($widget->id_base, ['new_entries', 'info_list'], true)) {
    return $instance;
  }

  $instance['history'] = !empty($new_instance['history']) ? 1 : 0;

  return $instance;
}, 10, 4);


// フロントエンド表示に反映
add_filter('widget_display_callback', function ($instance, $widget, $args) {
  // 対象外は終了
  if (!in_array($widget->id_base, ['new_entries', 'info_list'], true)) {
    return $instance;
  }

  $GLOBALS['history'] = !empty($instance['history']);

  return $instance;
}, 10, 3);


// ウィジェット描画直前に実行
add_action('dynamic_sidebar', function ($widget) {
  $GLOBALS['history'] = false;
}, 1);


// ウィジェットエリア出力直後に実行
add_action('dynamic_sidebar_after', function ($index, $has_widget) {
  $GLOBALS['history'] = false;
}, 10, 2);

拡張:「閲覧履歴をリセット」ボタンを追加

「閲覧履歴」機能を拡張し、「閲覧履歴をリセット」ボタンを追加します。

使用方法

以下にショートコードを示します。

[my_clear_history_button]

実装手順

以下のコードをfunctions.phpに追加します。

add_shortcode('my_clear_history_button', function () {
  ob_start();
  ?>
  <style>
  .history-clear {
    text-align: center;
  }

  .history-clear__button {
    background-color: #ccc;
    border-color: #ccc;
    pointer-events: none;
  }

  .history-clear__button.is-active {
    background-color: #ff0000;
    border-color: #ff0000;
    cursor: pointer;
    pointer-events: auto;
  }
  </style>

  <script>
  jQuery(function ($) {
    const cookieHistoryKey = 'my_post_history';

    /**
     * 指定されたキーのクッキー値を取得
     *
     * @param {string} key 取得したいクッキーのキー名
     * @return {string} クッキーの値(存在しない場合は空文字)
     */
    function getHistoryCookie(key) {
      const cookies = document.cookie.split('; ');

      for (let i = 0; i < cookies.length; i++) {
        const parts = cookies[i].split('=');

        if (parts[0] === key) {
          return decodeURIComponent(parts[1]);
        }
      }

      return '';
    }

    /**
     * 閲覧履歴クッキーの有無を判定し、リセットボタンの活性・非活性状態を切り替える
     *
     * @return {void}
     */
    function checkHistoryCookie() {
      const cookieVal = getHistoryCookie(cookieHistoryKey);
      let hasHistory = false;

      try {
        if (cookieVal && JSON.parse(cookieVal).length > 0) {
          hasHistory = true;
        }
      } catch (e) {}

      if (hasHistory) {
        $('.history-clear__button').addClass('is-active');
      } else {
        $('.history-clear__button').removeClass('is-active');
      }
    }

    checkHistoryCookie();

    $(document).on('click', '.history-clear__button', function () {
      // クッキーを削除
      document.cookie = cookieHistoryKey + '=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/;';

      // ボタンの活性化を解除
      $(this).removeClass('is-active');

      // ページをリロード
      location.href = location.pathname + '?history=' + Date.now();
    });
  });
  </script>

  <div class="history-clear">
    <button class="history-clear__button btn">閲覧履歴をリセット</button>
  </div>
  <?php
  return ob_get_clean();
});

さいごに

今回紹介した方法を使えば、閲覧した投稿ページを簡単にクッキーに記録し、リアルタイムで履歴を反映する機能を実装できます。
この機能を活用することで、ユーザーの利便性を向上させ、より魅力的なウェブサイトを構築することができます。

タイトルとURLをコピーしました