「カスタムHTML」ウィジェットに表示期間を設定し自動でパターンを切り替える方法

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

はじめに

キャンペーンバナーや期間限定のお知らせを表示する際、指定した時間になったら自動で表示・非表示を切り替えたいケースはよくあります。

以下の記事では、「カスタムHTML」ウィジェットごとに開始日時と終了日時を設定し、その指定期間だけコンテンツを表示させる方法を紹介しました。

今回は、独自の設定画面から期間ごとに「カスタムHTML」ウィジェットのパターンを置き換え表示します。

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

ウィジェット個別設定と一括管理の比較

ウィジェットごとに表示日時を設定する方法と、専用の設定ページを設けて一括管理する方法には、それぞれ以下のようなメリット・デメリットがあります(表1参照)。

個別設定と一括管理の比較
項目一括管理個別設定
運用・確認のしやすさ非常に楽
1つの画面で全スケジュールの俯瞰・変更が可能
大変
各ウィジェットを開いて個別に設定・確認が必要)
入力ミスのリスク低い
設定画面で一覧比較・一括保存できる
高い
複数ウィジェット間で設定の重複や漏れが起きやすい
表示位置の柔軟性1箇所のみ
全エリアで同一のスケジュールが動く
複数可能
サイドバーとフッターで別のタイマーを動かせる

完成イメージ

使用方法

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

パターンを自動切り替え表示したい位置に配置します。

[my_pattern_switcher]

実装手順

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

// 管理画面からの設定データ更新処理
add_action('admin_init', function () {
  // nonceチェック
  if (isset($_POST['action']) && $_POST['action'] === 'add_pattern') {
    check_admin_referer('pattern_switcher_action', 'pattern_switcher_nonce');
  }

  if (isset($_GET['action']) && $_GET['action'] === 'delete_pattern') {
    check_admin_referer('pattern_switcher_action', 'pattern_switcher_nonce');
  }

  // 権限チェック
  if (!current_user_can('manage_options')) {
    return;
  }

  // 新規データの保存処理
  if (isset($_POST['action']) && $_POST['action'] === 'add_pattern') {
    $options  = get_option('my_pattern_switcher', []);
    $patterns = is_array($options) ? $options : [];

    $pattern_id = isset($_POST['pattern_id'])
      ? sanitize_text_field($_POST['pattern_id'])
      : '';

    $st_date = isset($_POST['st_date'])
      ? sanitize_text_field($_POST['st_date'])
      : '';

    $ed_date = isset($_POST['ed_date'])
      ? sanitize_text_field($_POST['ed_date'])
      : '';

    // パターンIDを確認
    if ($pattern_id === '') {
      return;
    }

    $patterns[] = [
      'id'      => $pattern_id,
      'st_date' => $st_date,
      'ed_date' => $ed_date,
    ];

    update_option('my_pattern_switcher', array_values($patterns));

    set_transient('my_admin_notice', [
      'type'    => 'success',
      'message' => 'パターンを追加しました。',
    ], 30);

    wp_safe_redirect(admin_url('admin.php?page=pattern-switcher'));
    exit;
  }

  // 登録データの削除処理
  if (isset($_GET['action']) && $_GET['action'] === 'delete_pattern' && isset($_GET['index'])) {
    $options  = get_option('my_pattern_switcher', []);
    $patterns = is_array($options) ? $options : [];

    $index = absint($_GET['index']);

    // インデックスを確認
    if (isset($patterns[$index])) {
      unset($patterns[$index]);

      update_option('my_pattern_switcher', array_values($patterns));

      set_transient('my_admin_notice', [
        'type'    => 'success',
        'message' => 'パターンを削除しました。',
      ], 30);

      wp_safe_redirect(admin_url('admin.php?page=pattern-switcher'));
      exit;
    }
  }
});


// 管理画面にメニューを追加
add_action('admin_menu', function () {
  add_menu_page(
    'パターンスイッチャー設定',
    'パターンスイッチャー',
    'manage_options',
    'pattern-switcher',
    'render_pattern_switcher',
    'dashicons-clock',
    20
  );
});


// 通知メッセージを出力
add_action('admin_notices', function () {
  if ($notice = get_transient('my_admin_notice')) {
    echo '<div class="notice notice-' . esc_attr($notice['type']) . ' is-dismissible"><p>' . esc_html($notice['message']) . '</p></div>';
    delete_transient('my_admin_notice');
  }
});


/**
 * 管理画面設定ページを出力
 *
 * @return void
 */
if (!function_exists('render_pattern_switcher')):
function render_pattern_switcher() {
  // 権限チェック
  if (!current_user_can('manage_options')) {
    return;
  }

  // 保存済みの設定値を取得
  $options  = get_option('my_pattern_switcher', []);
  $patterns = is_array($options) ? $options : [];
  ?>
  <div class="wrap pattern-switcher">
    <h1 class="wp-heading-inline">パターンスイッチャー設定</h1>
    <hr class="wp-header-end">

    <p>各パターンIDと表示期間を設定してください。ウィジェット内の <code>[my_pattern_switcher]</code>を通じて自動切り替えされます。</p>

    <form method="post" action="" class="pattern-switcher__form">
      <?php wp_nonce_field('pattern_switcher_action', 'pattern_switcher_nonce'); ?>

      <input type="hidden" name="action" value="add_pattern">

      <table class="form-table">
        <tr>
          <th scope="row"><label for="pattern_id">パターンID</label></th>
          <td><input type="text" name="pattern_id" id="pattern_id" class="regular-text" placeholder="例: 123" required></td>
        </tr>
        <tr>
          <th scope="row"><label for="st_date">開始日時</label></th>
          <td><input type="datetime-local" name="st_date" id="st_date"></td>
        </tr>
        <tr>
          <th scope="row"><label for="ed_date">終了日時</label></th>
          <td><input type="datetime-local" name="ed_date" id="ed_date"></td>
        </tr>
      </table>

      <?php submit_button('パターンを追加'); ?>
    </form>

    <hr>

    <h2 class="pattern-switcher__heading">設定済み一覧</h2>

    <table class="wp-list-table widefat fixed striped table-view-list pattern-switcher__list">
      <thead>
        <tr>
          <th scope="col" style="width: 30%;">パターンID</th>
          <th scope="col" style="width: 30%;">開始日時</th>
          <th scope="col" style="width: 30%;">終了日時</th>
          <th scope="col" style="width: 10%; text-align: center;">操作</th>
        </tr>
      </thead>
      <tbody>
        <?php if (!empty($patterns)): ?>
          <?php foreach ($patterns as $index => $item): ?>
            <?php
            $pattern_id = !empty($item['id']) ? $item['id'] : '';
            $st_date    = !empty($item['st_date']) ? $item['st_date'] : '';
            $ed_date    = !empty($item['ed_date']) ? $item['ed_date'] : '';

            $delete_url = wp_nonce_url(
              admin_url('admin.php?page=pattern-switcher&action=delete_pattern&index=' . $index),
              'pattern_switcher_action',
              'pattern_switcher_nonce'
            );
            ?>

            <tr>
              <td><?= esc_html($pattern_id); ?></td>
              <td><?= esc_html($st_date); ?></td>
              <td><?= esc_html($ed_date); ?></td>
              <td style="text-align: center;"><a href="<?= esc_url($delete_url); ?>" class="button button-link-delete" onclick="return confirm('本当に削除しますか?');">削除</a></td>
            </tr>
          <?php endforeach; ?>
        <?php else: ?>
          <tr>
            <td colspan="4">設定されたパターンはありません。</td>
          </tr>
        <?php endif; ?>
      </tbody>
    </table>
  </div>
  <?php
}
endif;


// カスタムHTMLウィジェットの内容を変更
add_filter('widget_custom_html_content', function ($content, $instance, $widget) {
  // 保存済みの設定値を取得
  $options = get_option('my_pattern_switcher', []);

  // コンテンツ内に目的のショートコードが存在する場合のみ処理
  if (strpos($content, '[my_pattern_switcher]') !== false) {
    $rendered_html = '';

    if (!empty($options) && is_array($options)) {
      foreach ($options as $item) {
        // パターンIDが空の場合は処理対象外
        if (empty($item['id'])) {
          continue;
        }

        $pattern_id = esc_attr($item['id']);
        $st_date    = !empty($item['st_date']) ? esc_attr($item['st_date']) : '';
        $ed_date    = !empty($item['ed_date']) ? esc_attr($item['ed_date']) : '';

        // パターンコンテンツを取得
        $pattern_shortcode = '[pattern id="' . $pattern_id . '"]';
        $pattern_content   = do_shortcode($pattern_shortcode);

        // 表示期間を設定
        $rendered_html .= '<div class="pattern-switcher__item-content" data-start="' . $st_date . '" data-end="' . $ed_date . '" style="display:none;">' . $pattern_content . '</div>';
      }
    }

    $wrapper_html = '<div class="pattern-switcher">' . $rendered_html . '</div>';

    // ショートコードを置換
    $content = str_replace('[my_pattern_switcher]', $wrapper_html, $content);
  }

  // 残りのショートコードを実行
  return do_shortcode($content);
}, 10, 3);


// フロント用JS・CSSを追加
add_action('wp_enqueue_scripts', function () {
  ob_start();
  ?>
  jQuery(function ($) {
    $('.pattern-switcher').each(function () {
      const wrapper = $(this);

      // パターンコンテンツを取得
      const items = wrapper.find('.pattern-switcher__item-content');

      /**
       * 日時をタイムスタンプに変換
       *
       * @param {string|null} str 日時文字列
       * @returns {number|null} タイムスタンプ
       */
      const parseDate = function (str) {
        if (!str) {
          return null;
        }

        return new Date(str.replace(' ', 'T')).getTime();
      };

      /**
       * パターンの表示状態を更新
       *
       * @returns {void}
       */
      function updateSwitcher() {
        // 現在時刻を取得
        const now = new Date().getTime();
        let activeItem = null;
        let fallbackItem = null;

        // タイマー設定ありを優先して判定
        items.each(function () {
          const item  = $(this);
          const start = parseDate(item.data('start'));
          const end   = parseDate(item.data('end'));

          // タイマー設定なしを予備として保持
          if (!start && !end) {
            if (!fallbackItem) {
              fallbackItem = item;
            }
            return;
          }

          // 開始日時を判定
          const isStarted = !start || now >= start;

          // 終了日時を判定
          const isNotEnded = !end || now < end;

          // 表示期間を確認
          if (isStarted && isNotEnded && !activeItem) {
            activeItem = item;
          }
        });

        // タイマー設定ありを表示
        if (activeItem) {
          if (!activeItem.is(':visible')) {
            items.hide().removeClass('is-active');
            activeItem.show().addClass('is-active');
          }
          return;
        }

        // タイマー設定なしを表示
        if (fallbackItem) {
          if (!fallbackItem.is(':visible')) {
            items.hide().removeClass('is-active');
            fallbackItem.show().addClass('is-active');
          }
          return;
        }

        // 対象がない場合は非表示
        items.hide().removeClass('is-active');
      }

      updateSwitcher();

      // 1秒間隔で更新
      setInterval(updateSwitcher, 1000);
    });
  });
  <?php
  wp_add_inline_script('jquery', ob_get_clean());
});

補足

各パターンIDと表示期間を設定してください。ウィジェット内の[my_pattern_switcher]を通じて自動切り替えされます。

設定したパターンは、ページのリロード時にスケジュールが判定され、該当するパターンが表示されます。
また、タイマー設定のあるパターンが優先され、表示期間内のパターンがある場合は、タイマー設定のないパターンより優先して表示されます(表2参照)。

表示切り替えの例
パターン開始日時終了日時09:0012:0015:0018:00
通常パターン––表示表示表示表示
パターンA10:0012:00表示
パターンB14:0016:00表示

同じ時間帯に複数のタイマー設定がある場合は、登録順で判定され、先に該当したパターンが表示されます。

さいごに

今回紹介した方法を使えば、独自の設定画面から簡単に「カスタムHTML」ウィジェットの表示パターンや切り替えスケジュールを一括管理できます。
サイドバーやヘッダーなどの重要枠で、手動更新の手間をかけずにリアルタイムなタイムセール告知や期間限定キャンペーンを展開したい場合に非常に効果的です。

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