カスタム投稿タイプを用いて「ランキング機能」をWordPress標準化する方法

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

はじめに

Cocoonのランキング機能(Cocoon設定→ランキング)は独自画面と独自テーブル(wp_cocoon_item_rankings)で管理されています。

一方、パターンはカスタム投稿タイプ(wp_block)で、WordPress標準の投稿一覧などから管理できます。

そこで、ランキング機能もカスタム投稿タイプ(ranking_group)として、WordPress標準で管理するように変更します。
ランキングのアイテムはドラッグで並び順を変更することができます。
その際、rankショートコードの結果と同じ形式で独自のショートコードを作成し、評価数でソート可能とします。

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

完成イメージ

ランキング一覧
ランキング編集画面

使用方法

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

[my_ranking_manager]
オプション
オプション説明値初期値
idランキングID(必須)0
sort評価順でソートする0=なし / 1=あり0

実装手順

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

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

  • my-ranking-manager
    • ranking-manager.php
    • assets
      • css
        • admin.css
      • js
        • admin.js
    • includes
      • admin-ui.php
      • cpt.php
      • save.php
      • shortcode.php
  • ラベル
    PHPを追加

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

    <?php
    /**
     * Plugin Name: My Ranking Manager
     * Description: Cocoon用ランキング管理・表示プラグイン
     * Version: 1.0.0
     * Author: CHU-YA
     */
    
    if (!defined('ABSPATH')) {
      exit;
    }
    
    // 定数定義
    define('RM_PATH', plugin_dir_path(__FILE__));
    define('RM_URL', plugin_dir_url(__FILE__));
    
    
    // ファイル読み込み
    require_once RM_PATH . 'includes/cpt.php';
    require_once RM_PATH . 'includes/admin-ui.php';
    require_once RM_PATH . 'includes/save.php';
    require_once RM_PATH . 'includes/shortcode.php';
  • ラベル
    PHPを追加

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

    <?php
    if (!defined('ABSPATH')) {
      exit;
    }
    
    /**
     * ランキングアイテムの評価入力欄を生成
     *
     * @param int|string $i アイテムのインデックス
     * @param string $current 現在の評価値
     * @return string 評価入力欄のHTML
     */
    if (!function_exists('ranking_rating_html')):
    function ranking_rating_html($i, $current = 'none') {
      $ratings = [
        'none' => 'なし',
        '0'    => '0',
        '0.5'  => '0.5',
        '1'    => '1',
        '1.5'  => '1.5',
        '2'    => '2',
        '2.5'  => '2.5',
        '3'    => '3',
        '3.5'  => '3.5',
        '4'    => '4',
        '4.5'  => '4.5',
        '5'    => '5',
      ];
    
      ob_start();
      ?>
      <div class="ranking-manager__rating">
        <label style="float:left;">評価:</label>
        <ul>
          <?php foreach ($ratings as $value => $label): ?>
            <?php $checked = ((string) $current === (string) $value) ? ' checked' : ''; ?>
            <li>
              <label>
                <input type="radio" name="item_ranking[<?= esc_attr($i); ?>][rating]" value="<?= esc_attr($value); ?>"<?= $checked; ?>>
                <?= esc_html($label); ?>
              </label>
            </li>
          <?php endforeach; ?>
        </ul>
        <p class="tips">
          <span class="fa fa-info-circle" aria-hidden="true"></span>
          商品等を星の数で評価します。「なし」を選択した場合は表示されません。
        </p>
      </div>
      <?php
      return ob_get_clean();
    }
    endif;
    
    
    /**
     * ランキングアイテムの入力フォームを生成
     *
     * @param int|string $i アイテムインデックス
     * @param array $item アイテムのデータ
     * @param bool $is_closed 初期状態でフォームを閉じるか
     * @return string ランキングアイテムのHTML
     */
    if (!function_exists('ranking_item_html')):
    function ranking_item_html($i, $item = [], $is_closed = false) {
      $item = wp_parse_args($item, [
        'name'        => '',
        'rating'      => 'none',
        'image_tag'   => '',
        'description' => '',
        'detail_url'  => '',
        'link_url'    => '',
        'link_tag'    => '',
      ]);
    
      $closed_class = $is_closed ? ' is-closed' : '';
    
      $rank_num = is_numeric($i) ? ($i + 1) : '';
      $rank_text = $rank_num ? $rank_num . '位:' : '';
    
      ob_start();
      ?>
      <div class="ranking-manager__item<?= esc_attr($closed_class); ?>">
        <div class="ranking-manager__drag">
          <span class="ranking-manager__grip">⋮⋮</span>
          <span class="ranking-manager__rank"><?= esc_html($rank_text); ?></span>
          <span class="ranking-manager__title"><?= esc_html($item['name']); ?></span>
          <span class="ranking-manager__toggle-icon dashicons dashicons-arrow-down-alt2"></span>
        </div>
        <div class="ranking-manager__form">
          <div class="ranking-manager__name">
            <input type="text" name="item_ranking[<?= esc_attr($i); ?>][name]" value="<?= esc_attr($item['name']); ?>" placeholder="商品名等(必須)">
            <p class="tips"><span class="fa fa-info-circle" aria-hidden="true"></span> 項目の名前です。タグ・ショートコード入力も可能です。</p>
          </div>
    
          <?= ranking_rating_html($i, $item['rating']); ?>
    
          <div class="ranking-manager__image-description">
            <div>
              <label>画像・バナータグ</label><br>
              <textarea name="item_ranking[<?= esc_attr($i); ?>][image_tag]" rows="3"><?= esc_textarea($item['image_tag']); ?></textarea>
              <p class="tips"><span class="fa fa-info-circle" aria-hidden="true"></span> イメージ画像のタグを入力してください。※入力していない場合は表示されません。</p>
            </div>
            <div>
              <label>説明文(必須)</label><br>
              <textarea name="item_ranking[<?= esc_attr($i); ?>][description]" rows="3"><?= esc_textarea($item['description']); ?></textarea>
              <p class="tips"><span class="fa fa-info-circle" aria-hidden="true"></span> 紹介文を入力してください。タグ・ショートコード入力も可能です。</p>
            </div>
          </div>
    
          <div class="ranking-manager__link-buttons">
            <div>
              <label>詳細ページURL</label><br>
              <input type="text" name="item_ranking[<?= esc_attr($i); ?>][detail_url]" value="<?= esc_attr($item['detail_url']); ?>" placeholder="http://">
              <p class="tips"><span class="fa fa-info-circle" aria-hidden="true"></span> 詳細ページへリンクするためのURLを入力してください。</p>
            </div>
            <div>
              <label>公式ページURL</label><br>
              <input type="text" name="item_ranking[<?= esc_attr($i); ?>][link_url]" value="<?= esc_attr($item['link_url']); ?>" placeholder="http://">
              <p class="tips"><span class="fa fa-info-circle" aria-hidden="true"></span> 公式ページ等のURLを入力してください。「リンクタグ」と双方入力されている場合はこちらが優先されます。</p>
              <div class="ranking-manager__toggle">
                <span class="ranking-manager__toggle-link">タグで入力</span>
                <div class="ranking-manager__toggle-content">
                  <textarea name="item_ranking[<?= esc_attr($i); ?>][link_tag]" rows="3" placeholder="公式ページ等のリンク(アフィリエイト)タグを入力してください。"><?= esc_textarea($item['link_tag']); ?></textarea>
                  <p class="tips"><span class="fa fa-info-circle" aria-hidden="true"></span> アフィリエイトタグを直接入力する場合。タグ変更が無効なASP用設定です。ショートコードも入力できます。</p>
                </div>
              </div>
            </div>
          </div>
    
          <button type="button" class="ranking-manager__remove">削除</button>
        </div>
      </div>
      <?php
      return ob_get_clean();
    }
    endif;
    
    
    /**
     * メタボックスの入力フォームを出力
     *
     * @param WP_Post $post 投稿オブジェクト
     */
    if (!function_exists('render_ranking_group')):
    function render_ranking_group($post) {
      $post_id = $post->ID;
    
      wp_nonce_field('ranking_group_nonce', 'ranking_group_nonce');
    
      // 保存済みデータがある場合にのみ表示
      $meta_data = get_post_meta($post_id, 'item_ranking', true);
    
      if ($post_id && get_post_status($post_id) !== 'auto-draft' && !empty($meta_data)) {
        ?>
        <div class="admin-settings">
          <div class="demo">
            <?= do_shortcode('[my_rank id="' . esc_attr($post_id) . '"]'); ?>
          </div>
        </div>
        <?php
      }
    
      // POST値を優先
      if (isset($_POST['item_ranking'])) {
        $items = $_POST['item_ranking'];
      } else {
        $items = $meta_data;
      }
    
      if (!$items || !is_array($items)) {
        $items = [];
      }
    
      // 新規作成時は空フォーム1件表示
      if (get_post_status($post_id) === 'auto-draft' && empty($items)) {
        $items = [[]];
      }
    
      // JavaScriptでインデックスを置換するアイテム追加用テンプレートHTMLを生成
      $template_html = ranking_item_html('{i}', [], false);
      ?>
      <div id="ranking-manager-items-list" data-template="<?= esc_attr($template_html); ?>">
        <?php foreach ($items as $i => $item): ?>
          <?= ranking_item_html($i, $item, true); ?>
        <?php endforeach; ?>
      </div>
    
      <p style="margin-top: 20px;">
        <button type="button" class="button button-primary" id="ranking-manager-add-item">アイテム追加</button>
      </p>
      <?php
    }
    endif;
    
    
    // メタボックスを追加
    add_action('add_meta_boxes', function () {
      add_meta_box(
        'ranking_group',
        'ランキングアイテム',
        'render_ranking_group',
        'ranking_group',
        'normal',
        'high'
      );
    });
    
    
    // 管理画面用JS・CSSを追加
    add_action('admin_enqueue_scripts', function ($hook_suffix) {
      global $post;
    
      if (!$post || $post->post_type !== 'ranking_group') {
        return;
      }
    
      $parent_dir_uri = get_template_directory_uri();
    
      wp_enqueue_style('cocoon-fontawesome', $parent_dir_uri . '/webfonts/fontawesome/css/font-awesome.min.css', []);
      wp_enqueue_style('cocoon-admin-style', $parent_dir_uri . '/css/admin.css', []);
      wp_enqueue_style('ranking-manager-admin', RM_URL . 'assets/css/admin.css', [], '1.0.0');
    
      wp_enqueue_script('jquery-ui-sortable');
    
      wp_enqueue_script('ranking-manager-admin', RM_URL . 'assets/js/admin.js', ['jquery', 'jquery-ui-sortable'], '1.0.0', true);
    });
  • ラベル
    PHPを追加

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

    <?php
    if (!defined('ABSPATH')) {
      exit;
    }
    
    // カスタム投稿タイプの登録
    add_action('init', function () {
      register_post_type('ranking_group', [
        'label'         => 'ランキング',
        'menu_icon'     => 'dashicons-chart-bar',
        'menu_position' => 25,
        'public'        => false,
        'show_in_menu'  => true,
        'show_in_rest'  => true,
        'show_ui'       => true,
        'supports'      => ['title'],
      ]);
    });
    
    
    // 管理画面一覧にカラムを追加
    add_filter('manage_ranking_group_posts_columns', function ($columns) {
      $columns['shortcode'] = 'ショートコード';
    
      return $columns;
    });
    
    
    // 管理画面一覧のカラムにデータを表示
    add_action('manage_ranking_group_posts_custom_column', function ($column_name, $post_id) {
      if ($column_name === 'shortcode') {
        echo '<input type="text" readonly class="widefat" onclick="this.select();" value="[my_rank id=&quot;' . esc_attr($post_id) . '&quot;]">';
      }
    }, 10, 2);
  • ラベル
    PHPを追加

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

    <?php
    if (!defined('ABSPATH')) {
      exit;
    }
    
    // 投稿を保存
    add_action('save_post_ranking_group', function ($post_id) {
      // nonceチェック
      if (!isset($_POST['ranking_group_nonce']) || !wp_verify_nonce($_POST['ranking_group_nonce'], 'ranking_group_nonce')) {
        return;
      }
    
      // 自動保存チェック
      if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
        return;
      }
    
      // リビジョンは除外
      if (wp_is_post_revision($post_id)) {
        return;
      }
    
      // 権限チェック
      if (!current_user_can('edit_post', $post_id)) {
        return;
      }
    
      // データの取得とサニタイズ
      if (isset($_POST['item_ranking']) && is_array($_POST['item_ranking'])) {
        $sanitized_items = [];
    
        foreach ($_POST['item_ranking'] as $item) {
          if (!is_array($item)) {
            continue;
          }
    
          $sanitized_items[] = [
            'name'        => isset($item['name'])         ? sanitize_text_field($item['name'])    : '',
            'rating'      => isset($item['rating'])       ? sanitize_text_field($item['rating'])  : 'none',
            'image_tag'   => isset($item['image_tag'])    ? wp_kses_post($item['image_tag'])      : '',
            'description' => isset($item['description'])  ? wp_kses_post($item['description'])    : '',
            'detail_url'  => isset($item['detail_url'])   ? esc_url_raw($item['detail_url'])      : '',
            'link_url'    => isset($item['link_url'])     ? esc_url_raw($item['link_url'])        : '',
            'link_tag'    => isset($item['link_tag'])     ? wp_kses_post($item['link_tag'])       : '',
          ];
        }
    
        update_post_meta($post_id, 'item_ranking', $sanitized_items);
      } else {
        delete_post_meta($post_id, 'item_ranking');
      }
    });
  • ラベル
    PHPを追加

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

    <?php
    if (!defined('ABSPATH')) {
      exit;
    }
    
    // ショートコードを追加
    add_shortcode('my_rank', function ($atts) {
      $atts = shortcode_atts([
        'id'   => get_the_ID(),
        'sort' => 0,
      ], $atts);
    
      $post_id = intval($atts['id']);
      $sort    = intval($atts['sort']);
    
      $items = get_post_meta($post_id, 'item_ranking', true);
    
      if (empty($items) || !is_array($items)) {
        return '';
      }
    
      $items = array_values(array_filter($items, function ($item) {
        return is_array($item) && !empty($item);
      }));
    
      if (empty($items)) {
        return '';
      }
    
      // 評価でソート
      if ($sort) {
        $items = rm_sort_items_by_rating($items);
      }
    
      // アイテム数を取得
      $count = count($items);
    
      ob_start();
      ?>
      <div class="ranking-items no-icon">
        <?php
        for ($i = 0; $i < $count; $i++) :
          // 各アイテムデータ取得
          $item = $items[$i];
    
          $name        = $item['name']        ?? '';
          $rating      = $item['rating']      ?? 'none';
          $image_tag   = $item['image_tag']   ?? '';
          $description = $item['description'] ?? '';
          $detail_url  = $item['detail_url']  ?? '';
          $link_url    = $item['link_url']    ?? '';
          $link_tag    = $item['link_tag']    ?? '';
    
          // 改行除去
          $name      = preg_replace('/\n/', '', $name);
          $image_tag = preg_replace('/\n/', '', $image_tag);
          $link_tag  = preg_replace('/\n/', '', $link_tag);
    
          // Cocoon互換フィルター適用
          $name        = apply_filters('ranking_item_name', $name);
          $image_tag   = apply_filters('ranking_item_image_tag', $image_tag);
          $description = apply_filters('ranking_item_description', wpautop($description));
          $link_tag    = apply_filters('ranking_item_link_tag', $link_tag);
    
          // 表示用順位番号
          $rank_no = $i + 1;
          ?>
    
          <div class="ranking-item-<?php echo $rank_no; ?> ranking-item border-element">
    
            <div class="ranking-item-name">
              <div class="ranking-item-name-crown">
                <?php generate_ranking_crown_tag($rank_no); ?>
              </div>
              <div class="ranking-item-name-text">
                <?php echo $name; ?>
              </div>
            </div>
    
            <?php if ($rating !== 'none') : ?>
              <div class="ranking-item-rating">
                <?php echo get_rating_star_tag($rating); ?>
              </div>
            <?php endif; ?>
    
            <div class="ranking-item-img-desc">
    
              <?php if ($image_tag) : ?>
                <div class="ranking-item-image-tag">
                  <?php echo $image_tag; ?>
                </div>
              <?php endif; ?>
    
              <div class="ranking-item-description">
                <?php echo $description; ?>
              </div>
    
            </div>
    
            <?php if ($detail_url || $link_url || $link_tag) : ?>
              <div class="ranking-item-link-buttons">
    
                <?php if ($detail_url) :
                  $target = apply_filters('cocoon_ranking_detail_page_target', '_self');
                  ?>
                  <div class="ranking-item-detail">
                    <a href="<?php echo esc_url($detail_url); ?>" target="<?php echo esc_attr($target); ?>">
                      <?php echo apply_filters('cocoon_ranking_detail_page_caption', __('詳細ページ', THEME_NAME)); ?>
                    </a>
                  </div>
                <?php endif; ?>
    
                <?php if ($link_url || $link_tag) : ?>
                  <div class="ranking-item-link">
                    <?php if ($link_url) :
                      $target = apply_filters('cocoon_ranking_official_page_target', '_blank');
                      ?>
                      <a href="<?php echo esc_url($link_url); ?>" target="<?php echo esc_attr($target); ?>">
                        <?php echo apply_filters('cocoon_ranking_official_page_caption', __('公式ページ', THEME_NAME)); ?>
                      </a>
                    <?php else : ?>
                      <?php echo $link_tag; ?>
                    <?php endif; ?>
                  </div>
                <?php endif; ?>
    
              </div>
            <?php endif; ?>
    
          </div>
    
        <?php endfor; ?>
      </div>
      <?php
      return ob_get_clean();
    });
    
    
    /**
     * 評価順のランキング配列を取得
     *
     * @param array $items ランキングアイテム配列
     * @return array 評価降順に並び替えたランキングアイテム配列
     */
    if (!function_exists('rm_sort_items_by_rating')):
    function rm_sort_items_by_rating($items) {
      // usortで評価降順ソート
      usort($items, function ($a, $b) {
        $ra = isset($a['rating']) && $a['rating'] !== 'none' ? (float) $a['rating'] : -1;
        $rb = isset($b['rating']) && $b['rating'] !== 'none' ? (float) $b['rating'] : -1;
    
        if ($ra === $rb) {
          return 0;
        }
    
        return ($ra > $rb) ? -1 : 1;
      });
    
      return $items;
    }
    endif;
    
  • ラベル
    JavaScriptを追加

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

    jQuery(function ($) {
    
      // ランキングアイテム一覧の取得
      var $list = $('#ranking-manager-items-list');
      if (!$list.length) return;
    
      // 新規アイテム用テンプレートの取得
      var template = $list.data('template');
    
      // 新規アイテム用インデックスの初期値
      var i = $list.children('.ranking-manager__item').length;
    
      // 未保存の変更状態
      var isChanged = false;
    
    
      /**
       * アイテムの並び順に合わせたname属性と順位表示の更新
       *
       * @returns {void}
       */
      function renumber() {
        var $items = $list.children('.ranking-manager__item');
    
        // name属性を一時的な名前へ変更して重複を防止
        $items.each(function (idx) {
          $(this).find('input, textarea').each(function () {
            this.name = this.name.replace(/\[\d+\]/, '[tmp' + idx + ']');
          });
        });
    
        // 正式な番号の設定と順位表示の更新
        $items.each(function (idx) {
          $(this).find('input, textarea').each(function () {
            this.name = this.name.replace(/\[tmp\d+\]/, '[' + idx + ']');
          });
    
          $(this).find('.ranking-manager__rank').text((idx + 1) + '位:');
        });
      }
    
    
      /**
       * 投稿保存時の入力内容を検証
       *
       * @param {Event} e フォーム送信イベント
       * @returns {boolean|void} 入力エラー時はfalse
       */
      $('#post').on('submit', function (e) {
    
        let hasError = false;
        let errorMessage = '';
    
        // タイトルの必須チェック
        const title = $('#title').val()?.trim();
    
        if (!title) {
          errorMessage += 'タイトルは必須です。\n';
          $('#title').css('border-color', '#d63638');
          hasError = true;
        } else {
          $('#title').css('border-color', '');
        }
    
        let itemsCount = 0;
    
        // ランキングアイテムの必須項目をチェック
        $('#ranking-manager-items-list .ranking-manager__item').each(function () {
          itemsCount++;
    
          const $item = $(this);
          const name = $item.find('[name*="[name]"]').val()?.trim();
          const desc = $item.find('[name*="[description]"]').val()?.trim();
    
          if (!name || !desc) {
            hasError = true;
    
            $item
              .css('border', '1px solid #b32d2e')
              .addClass('ranking-manager__error');
    
            // 入力エラーのあるアイテムを展開
            $item.removeClass('is-closed').find('.ranking-manager__form').show();
          } else {
            $item
              .css('border', '')
              .removeClass('ranking-manager__error');
          }
        });
    
        // エラーメッセージの設定
        if (itemsCount === 0) {
          errorMessage += 'ランキングアイテムを1件以上追加してください。\n';
          hasError = true;
        } else if (hasError && errorMessage === '') {
          errorMessage += '「商品名等」と「説明文」は必須です。\n';
        }
    
        // 入力エラー時の保存中止
        if (hasError) {
          alert(errorMessage);
          $('#publish').removeClass('disabled');
          $('#save-post').removeClass('disabled');
          $('.spinner').removeClass('is-active');
          e.preventDefault();
          return false;
        }
    
        // 保存開始時の未保存フラグ解除
        isChanged = false;
      });
    
    
      /**
       * ドラッグ&ドロップによるランキングアイテムの並び替えを初期化
       */
      $list.sortable({
        handle: '.ranking-manager__drag',
        placeholder: 'ranking-manager__placeholder',
        forcePlaceholderSize: true,
    
        /**
         * ドラッグ用クローンを生成
         *
         * @param {Event} e ドラッグイベント
         * @param {JQuery} item ドラッグ対象のjQueryオブジェクト
         * @returns {JQuery} ドラッグ用クローン
         */
        helper: function (e, item) {
          // クローンのラジオボタンの競合を防止
          return item.clone()
            .find('input[type="radio"], input[type="checkbox"]')
            .removeAttr('name')
            .end();
        },
    
        tolerance: 'pointer',
    
        /**
         * ドラッグ開始時のプレースホルダー高さを調整
         *
         * @param {Event} e ドラッグイベント
         * @param {Object} ui jQuery UI SortableのUIオブジェクト
         * @returns {void}
         */
        start: function (e, ui) {
          ui.placeholder.height(ui.item.outerHeight());
        },
    
        /**
         * 最下部へ移動した場合のプレースホルダー位置を調整
         *
         * @param {Event} e ソートイベント
         * @param {Object} ui jQuery UI SortableのUIオブジェクト
         * @returns {void}
         */
        sort: function (e, ui) {
          var $last = $list.children('.ranking-manager__item').last();
    
          if (ui.offset.top > $last.offset().top + $last.outerHeight() / 2) {
            $list.append(ui.placeholder);
          }
        },
    
        /**
         * 並び替え完了後の状態を更新
         *
         * @returns {void}
         */
        update: function () {
          renumber();
          isChanged = true;
        }
      });
    
    
      /**
       * 商品名の入力内容をアイテムヘッダーへ反映
       *
       * @param {Event} e 入力イベント
       * @returns {void}
       */
      $(document).on('input', '#ranking-manager-items-list .ranking-manager__name input', function (e) {
        var val = $(this).val().trim();
        var $headerTitle = $(this)
          .closest('.ranking-manager__item')
          .find('.ranking-manager__title');
    
        // アイテムヘッダーのタイトル更新
        $headerTitle.text(val);
      });
    
    
      /**
       * アイテムヘッダーのクリックによるフォーム開閉
       *
       * @param {Event} e クリックイベント
       * @returns {void}
       */
      $(document).on('click', '.ranking-manager__drag', function (e) {
        if ($(e.target).closest('.ranking-manager__remove').length) return;
    
        var $item = $(this).closest('.ranking-manager__item');
        var $form = $item.find('.ranking-manager__form');
    
        if ($item.hasClass('is-closed')) {
          $item.removeClass('is-closed');
          $form.hide().slideDown(200);
        } else {
          $item.addClass('is-closed');
          $form.slideUp(200);
        }
      });
    
    
      /**
       * 「タグで入力」のクリックによる開閉
       *
       * @param {Event} e クリックイベント
       * @returns {void}
       */
      $(document).on('click', '.ranking-manager__toggle-link', function (e) {
        e.preventDefault();
    
        $(this)
          .siblings('.ranking-manager__toggle-content')
          .stop(true, true)
          .slideToggle(200);
      });
    
    
      /**
       * 新しいランキングアイテムを追加
       *
       * @returns {void}
       */
      $('#ranking-manager-add-item').on('click', function () {
        var $newItem = $(template.replace(/\{i\}/g, i));
    
        $list.append($newItem);
        renumber();
    
        i++;
        isChanged = true;
      });
    
    
      /**
       * ランキングアイテムを削除
       *
       * @param {Event} e クリックイベント
       * @returns {void}
       */
      $(document).on('click', '.ranking-manager__remove', function (e) {
        e.stopPropagation();
    
        if (confirm('削除しますか?')) {
          var $item = $(this).closest('.ranking-manager__item');
          var index = $item.index();
    
          $item.remove();
          renumber();
          isChanged = true;
    
          // プレビューから削除したアイテムを非表示
          var $previewItems = $('.demo .ranking-items > .ranking-item');
    
          if ($previewItems.length <= 1) {
            $('.admin-settings').fadeOut(300);
          } else {
            $previewItems.eq(index).fadeOut(300);
          }
        }
      });
    
    
      /**
       * フォーム内容の変更状態を更新
       *
       * @param {Event} e 入力・変更イベント
       * @returns {void}
       */
      $(document).on(
        'input change',
        '#ranking-manager-items-list input, #ranking-manager-items-list textarea',
        function (e) {
          isChanged = true;
        }
      );
    
    
      /**
       * 未保存の変更がある場合のページ離脱警告
       *
       * @returns {string|undefined} 離脱警告メッセージ
       */
      $(window).on('beforeunload', function () {
        if (isChanged) {
          return '変更が保存されていません。';
        }
      });
    
    });
  • ラベル
    CSSを追加

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

    @charset "UTF-8";
    
    #poststuff .inside {
      margin: 20px 0 0;
    }
    
    /* プレビュー */
    .admin-settings .demo {
      background-color: #fff;
      border: 2px solid #ccc;
      height: 400px;
      max-width: 1000px;
      margin: 20px 0;
      padding: 20px;
    }
    
    .tips {
      margin: 4px 0 12px!important;
    }
    
    /* アイテム */
    .ranking-manager__item {
      border: 2px solid #ccc;
      font-size: 14px;
      margin-bottom: 20px;
    }
    
    /* ドラッグバー(アコーディオンヘッダー) */
    .ranking-manager__drag {
      background-color: #eee;
      border-bottom: 1px solid #ccc;
      cursor: move;
      padding: 10px 20px;
      display: flex;
      align-items: center;
      gap: 10px;
      user-select: none;
    }
    
    .ranking-manager__item.is-closed .ranking-manager__drag {
      border-bottom: 0;
    }
    
    /* アイコン */
    .ranking-manager__grip {
      color: #8c8f94;
      font-size: 14px;
      font-weight: bold;
      letter-spacing: -2px;
      margin-right:0.5em;
    }
    
    /* 順位 */
    .ranking-manager__rank {
      font-weight: bold;
      white-space: nowrap;
    }
    
    /* 商品名 */
    .ranking-manager__title {
      font-weight: bold;
      color: #333;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }
    
    /* 開閉トグルアイコン */
    .ranking-manager__toggle-icon {
      margin-left: auto;
      transition: transform 0.2s ease;
    }
    
    /* 閉じているときはアイコンを反転 */
    .ranking-manager__item.is-closed .ranking-manager__toggle-icon {
      transform: rotate(180deg);
    }
    
    /* ランキングフォーム */
    .ranking-manager__form {
      padding: 20px;
    }
    
    .ranking-manager__item.is-closed .ranking-manager__form {
      display: none;
    }
    
    /* 評価 */
    .ranking-manager__rating ul {
      list-style: none;
      margin: 0;
    }
    
    .ranking-manager__rating li {
      display: inline-block;
      margin-right: 5px;
    }
    
    .ranking-manager__item textarea {
      width: 100%;
    }
    
    /* 画像、ページURL */
    .ranking-manager__image-description,
    .ranking-manager__link-buttons {
      display: flex;
      gap: 20px;
    }
    
    .ranking-manager__image-description > div,
    .ranking-manager__link-buttons > div {
      width: 100%;
    }
    
    /* タグ入力 */
    .ranking-manager__toggle-content {
      display: none;
    }
    
    .ranking-manager__toggle .ranking-manager__toggle-link {
      color: #2271b1;
      font-size: unset!important;
      margin-left: 0!important;
      text-decoration: underline;
      cursor: pointer;
    }
    
    /* 削除ボタン */
    .ranking-manager__item .ranking-manager__remove {
      background-color: #b32d2e;
      border: 0;
      border-radius: 3px;
      color: #fff;
      cursor: pointer;
      margin-top: 10px;
      padding: 4px 8px;
    }
    
    /* 移動枠 */
    .ranking-manager__placeholder {
      background-color: #f0f6fc;
      border: 2px dashed #007cba;
      margin-bottom: 20px;
    }

さいごに

今回紹介した方法を使えば、Cocoonのランキング機能をWordPress標準のカスタム投稿タイプとして管理でき、ドラッグ操作による並び替えやショートコードによる本文表示も簡単に実現できます。
これにより、従来の独自テーブル管理から解放され、管理画面での操作性も大幅に向上します。
評価数でのソートにも対応しているため、ランキング記事の制作効率も格段にアップします。

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