プラグインなしでウィジェットの表示制御する方法

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

Cocoonには、標準機能として備わっています。

はじめに

プラグイン「Widget Logic」で、2025年6月7日に以下の問題が報告されました。

そのため、プラグインを使わずに、ページなどの条件に応じてウィジェットの表示・非表示を制御する機能を追加します。

なお、Cocoonにはウィジェットの表示条件を設定できる独自機能が備わっています。

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

完成イメージ

実装手順

プラグインの形式で実装します。

以下の手順で実装します。

  • my-widget-visibility
    • widget-visibility.php
    • assets
      • css
        • admin.css
      • js
        • admin.js
  • ラベル
    PHPを追加

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

    <?php
    /**
     * Plugin Name: My Widget Visibility
     * Description: 投稿・固定ページ、カテゴリページなどの条件に応じウィジェットを表示制御します。
     * Version: 1.0.0
     * Author: CHU-YA
     */
    
    if (!defined('ABSPATH')) {
      exit;
    }
    
    // ブロックウィジェットエディターを無効化
    add_filter('use_widgets_block_editor', '__return_false');
    
    
    // 管理画面用JS・CSSを追加
    add_action('admin_enqueue_scripts', function ($hook_suffix) {
      if ($hook_suffix !== 'widgets.php') {
        return;
      }
    
      wp_enqueue_style('wv-widget-admin', plugin_dir_url(__FILE__) . 'assets/css/admin.css', [], '1.0.0');
    
      wp_enqueue_script('wv-widget-admin', plugin_dir_url(__FILE__) . 'assets/js/admin.js', ['jquery'], '1.0.0', true);
    });
    
    
    // ウィジェットに設定メニューを追加
    add_action('in_widget_form', function ($widget, $return, $instance) {
      $conditions = $instance['wv_conditions'] ?? [];
      $display_mode = $instance['wv_display_mode'] ?? 'show';
    
      $page_types = $conditions['page_types'] ?? [];
      $post_ids = $conditions['post_ids'] ?? '';
      $term_ids = $conditions['term_ids'] ?? '';
    
      $custom_post_types = get_post_types(
        ['public' => true, '_builtin' => false],
        'objects'
      );
    
      ?>
      <div class="wv-widget">
        <p><strong>ウィジェット表示制御</strong></p>
    
        <div class="wv-widget__mode">
          <label>
            <input type="radio" name="<?= esc_attr($widget->get_field_name('wv_display_mode')); ?>" value="show" <?php checked($display_mode, 'show'); ?>>
            一致する場合に表示
          </label><br>
    
          <label>
            <input type="radio" name="<?= esc_attr($widget->get_field_name('wv_display_mode')); ?>" value="hide" <?php checked($display_mode, 'hide'); ?>>
            一致する場合に非表示
          </label>
        </div>
    
        <ul class="wv-widget__tabs">
          <li class="wv-widget__tab is-active" data-target="tab-basic">基本設定</li>
          <?php if ($custom_post_types): ?>
            <li class="wv-widget__tab" data-target="tab-cpt">カスタム投稿ページ</li>
          <?php endif; ?>
          <li class="wv-widget__tab" data-target="tab-ids">ID指定</li>
          <li class="wv-widget__tab" data-target="tab-terms">タームID</li>
        </ul>
    
        <div class="wv-widget__contents">
    
          <div class="wv-widget__content is-active" data-content="tab-basic">
            <?php
            $basics = [
              'is_front_page' => 'フロントページ',
              'is_single'     => '標準投稿の詳細',
              'is_page'       => '固定ページ',
              'is_category'   => 'カテゴリー一覧',
              'is_tag'        => 'タグ一覧',
              'is_date'       => '日付別一覧',
              'is_author'     => '著者別一覧',
            ];
    
            foreach ($basics as $key => $label):
            ?>
              <label>
                <input type="checkbox" name="<?= esc_attr($widget->get_field_name('wv_conditions[page_types][]')); ?>" value="<?= esc_attr($key); ?>" <?php checked(in_array($key, $page_types, true)); ?>>
                <?= esc_html($label); ?>
              </label>
            <?php endforeach; ?>
          </div>
    
          <?php if ($custom_post_types): ?>
            <div class="wv-widget__content" data-content="tab-cpt">
              <?php foreach ($custom_post_types as $cpt): ?>
                <label>
                  <input type="checkbox" name="<?= esc_attr($widget->get_field_name('wv_conditions[page_types][]')); ?>" value="cpt_single_<?= esc_attr($cpt->name); ?>" <?php checked(in_array('cpt_single_' . $cpt->name, $page_types, true)); ?>>
                  <?= esc_html($cpt->label); ?>
                </label>
              <?php endforeach; ?>
            </div>
          <?php endif; ?>
    
          <div class="wv-widget__content" data-content="tab-ids">
            <p>投稿・固定・カスタム投稿ページID(カンマ区切り)</p>
            <input type="text" name="<?= esc_attr($widget->get_field_name('wv_conditions[post_ids]')); ?>" value="<?= esc_attr($post_ids); ?>" placeholder="例:1,2">
          </div>
    
          <div class="wv-widget__content" data-content="tab-terms">
            <p>タームID(カテゴリー・タグ・カスタム分類)</p>
            <input type="text" name="<?= esc_attr($widget->get_field_name('wv_conditions[term_ids]')); ?>" value="<?= esc_attr($term_ids); ?>" placeholder="例:1,2">
          </div>
    
        </div>
      </div>
      <?php
    }, 20, 3);
    
    
    // ウィジェットの入力データを更新
    add_filter('widget_update_callback', function ($instance, $new_instance) {
      $instance['wv_conditions'] = [];
    
      if (!empty($new_instance['wv_conditions'])) {
        foreach ($new_instance['wv_conditions'] as $key => $value) {
          $instance['wv_conditions'][$key] = is_array($value)
            ? array_map('sanitize_text_field', $value)
            : sanitize_text_field($value);
        }
      }
    
      $instance['wv_display_mode'] = $new_instance['wv_display_mode'] ?? 'show';
    
      return $instance;
    }, 10, 2);
    
    
    // フロントエンド表示に反映
    add_filter('widget_display_callback', function ($instance, $widget, $args) {
      // 表示条件なしは処理対象外
      if (!isset($instance['wv_conditions'])) {
        return $instance;
      }
    
      $conditions = $instance['wv_conditions'];
      $matched = false;
    
      // ページ種別を判定
      if (!empty($conditions['page_types'])) {
        foreach ($conditions['page_types'] as $page_type) {
          if (strpos($page_type, 'cpt_single_') === 0) {
            if (is_singular(str_replace('cpt_single_', '', $page_type))) {
              $matched = true;
              break;
            }
          } elseif (function_exists($page_type) && call_user_func($page_type)) {
            $matched = true;
            break;
          }
        }
      }
    
      // タームIDを判定
      if (!$matched && !empty($conditions['term_ids'])) {
        $term_ids = array_map('intval', explode(',', $conditions['term_ids']));
    
        if (is_category($term_ids) || is_tag($term_ids)) {
          $matched = true;
        } elseif (is_tax()) {
          $object = get_queried_object();
    
          if (!empty($object->term_id) && in_array((int) $object->term_id, $term_ids, true)) {
            $matched = true;
          }
        } elseif (is_singular()) {
          foreach (get_post_taxonomies() as $taxonomy) {
            if (has_term($term_ids, $taxonomy)) {
              $matched = true;
              break;
            }
          }
        }
      }
    
      // 投稿IDを判定
      if (!$matched && !empty($conditions['post_ids'])) {
        $post_ids = array_map('intval', explode(',', $conditions['post_ids']));
    
        if (is_singular() && (is_single($post_ids) || is_page($post_ids))) {
          $matched = true;
        }
      }
    
      $display_mode = $instance['wv_display_mode'] ?? 'show';
    
      // 表示状態を変更
      return (($display_mode === 'show' && !$matched) || ($display_mode === 'hide' && $matched))
        ? false
        : $instance;
    }, 20, 3);
  • ラベル
    JavaScriptを追加

    以下のコードをadmin.jsに追加します。

    jQuery(function ($) {
      $(document).on('click', '.wv-widget__tab', function () {
        var target = $(this).data('target');
        var $widget = $(this).closest('.wv-widget');
    
        // タブの状態を変更
        $widget.find('.wv-widget__tab').removeClass('is-active');
        $(this).addClass('is-active');
    
        // タブコンテンツの状態を変更
        $widget
          .find('.wv-widget__content')
          .removeClass('is-active')
          .filter('[data-content="' + target + '"]')
          .addClass('is-active');
      });
    });
  • ラベル
    CSSを追加

    以下のコードをadmin.cssに追加します。

    .wv-widget {
      border-top: 1px solid #ccc;
      margin-top: 20px;
    }
    
    .wv-widget__mode {
      margin-bottom: 20px;
    }
    
    .wv-widget__tabs {
      border-bottom: 1px solid #ccc;
      display: flex;
      flex-wrap: wrap;
      list-style: none;
      margin: 20px 0;
      padding: 0;
    }
    
    .wv-widget__tab {
      background-color: #eee;
      border: 1px solid #ccc;
      border-radius: 3px 3px 0 0;
      cursor: pointer;
      font-size: 11px;
      margin-bottom: -1px;
      margin-right: 2px;
      padding: 4px 8px;
    }
    
    .wv-widget__tab.is-active {
      background-color: #fff;
      border-bottom-color: #fff;
      font-weight: bold;
    }
    
    .wv-widget__content {
      display: none;
      margin-bottom: 20px;
    }
    
    .wv-widget__content.is-active {
      display: block;
    }
    
    .wv-widget__content label {
      display: block;
      font-size: 12px;
      margin-bottom: 4px;
    }
    
    .wv-widget__content input[type="text"] {
      border: 1px solid #ddd;
      box-sizing: border-box;
      padding: 5px;
      width: 100%;
    }

さいごに

今回紹介した方法を使えば、プラグインを使わずに、投稿・固定ページやカテゴリ・タグなどの条件に応じてウィジェットの表示・非表示を柔軟に制御できます。
独自プラグインとして実装することで、他のテーマでも再利用可能な拡張性の高い仕組みにできるのも大きな利点です。

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