はじめに
WordPressを用いた情報発信において、コンバージョン率の向上を目的としたCTA(Call To Action)の設置は重要な施策です。
特にブログやメディアサイトでは、読者の閲覧文脈に即したCTAを適切に表示することが、成果に直結します。
しかし、Cocoonテーマに標準搭載されている「CTA」ウィジェットは、表示条件ごとに個別のウィジェットを配置する必要があり、投稿単位やカテゴリー単位で設定を管理しなければなりません。
そのため、サイト規模の拡大に伴い設定の重複や修正漏れが発生しやすく、運用や保守の負担が大きくなるという課題があります。
そこで、従来のウィジェット型CTAが抱える運用上の問題点を整理し、それに対する設計思想と実装方針を明らかにすることで、本手法の有効性を検討することを目的としています。
この記事では、この方法について説明します。
目的
Cocoon標準の「CTA」ウィジェットを使ってカテゴリーごとに表示条件を分岐させる運用では、主に2つの課題が生じます。
- 表示条件ごとに「CTA」ウィジェットを複数配置する必要がある
カテゴリーが増えるたびにウィジェットエリアへ個別にCTAを追加し、それぞれ表示条件を設定しなければならず、配置作業が煩雑になります。
- ウィジェットエリア上でしか管理できず、全体像を一括把握できない
どのCTAがどのカテゴリーに対応しているのか、何がどこに配置されているのかを一覧で確認できないため、管理や見直しの負担が大きくなります。
今回紹介するコードを導入すれば、CTAの設定を「カスタム投稿タイプ(cta)」として一括管理できるようになり、投稿ページで該当カテゴリーに応じたCTAが自動表示されるようになります。
- ウィジェットを複数配置する必要がなく、カテゴリーに応じて自動切り替えが可能
- 管理画面のCTA一覧で設定状況を一括把握でき、追加・編集が容易になる
単純にカテゴリー一覧などに一律表示するのではなく、投稿本文の文脈に沿った適切なCTAを自動表示させることで、読者の関心や行動を自然に誘導し、コンバージョン率の最適化を図ることができます。
完成イメージ


実装手順
以下の手順で実装します。
- my-auto-cta
- auto-cta.php
- assets
- js
- admin.js
- js
- includes
- admin-ui.php
- cpt.php
- matcher.php
- preview.php
- renderer.php
- save.php
- widget.php
- ラベルPHPを追加
以下のコードをauto-cta.phpに追加します。
<?php /** * Plugin Name: My Auto CTA Manager * Description: Cocoon用CTAをカテゴリー条件に応じて自動表示します。 * Version: 1.0.0 * Author: CHU-YA */ if (!defined('ABSPATH')) { exit; } // 定数定義 define('AUTO_CTA_PATH', plugin_dir_path(__FILE__)); define('AUTO_CTA_URL', plugin_dir_url(__FILE__)); // Cocoon以外では警告表示 add_action('admin_notices', function () { if (stripos(get_template(), 'cocoon') !== false) { return; } echo '<div class="notice notice-error"><p><strong>CTA管理:</strong> このプラグインは Cocoon テーマ専用です。</p></div>'; }); // Cocoon以外は処理対象外 if (stripos(get_template(), 'cocoon') === false) { return; } // ファイル読み込み require_once AUTO_CTA_PATH . 'includes/admin-ui.php'; require_once AUTO_CTA_PATH . 'includes/cpt.php'; require_once AUTO_CTA_PATH . 'includes/matcher.php'; require_once AUTO_CTA_PATH . 'includes/preview.php'; require_once AUTO_CTA_PATH . 'includes/renderer.php'; require_once AUTO_CTA_PATH . 'includes/save.php'; require_once AUTO_CTA_PATH . 'includes/widget.php'; - ラベルPHPを追加
以下のコードをadmin-ui.phpに追加します。
<?php if (!defined('ABSPATH')) { exit; } // メタボックスを追加 add_action('add_meta_boxes', function () { add_meta_box( 'auto_cta', 'CTA設定', 'render_auto_cta', 'my_auto_cta', 'normal', 'high' ); }); // スラッグメタボックスを非表示 add_action('add_meta_boxes', function () { remove_meta_box('slugdiv', 'my_auto_cta', 'normal'); }); /** * メタボックスの入力フォームを出力 * * @param WP_Post $post 投稿オブジェクト * @return void */ if (!function_exists('render_auto_cta')): function render_auto_cta($post) { // CTAプレビューURLを取得 $preview_url = add_query_arg([ 'action' => 'auto_cta_preview', 'post_id' => $post->ID, ], admin_url('admin-ajax.php')); // CTAフィールドを取得 $fields = [ 'heading', 'image_url', 'layout', 'button_text', 'button_url', 'button_color', ]; foreach ($fields as $key) { $$key = get_post_meta($post->ID, 'my_auto_cta_' . $key, true); } // 選択済みカテゴリーを取得 $selected_terms = (array) get_post_meta($post->ID, 'my_auto_cta_target_categories', true); // ボタンカラーリストを設定 $button_colors = [ 'btn-black' => '黒色', 'btn-red' => '赤色', 'btn-pink' => 'ピンク', 'btn-purple' => '紫色', 'btn-deep' => '深紫', 'btn-indigo' => '紺色(インディゴ)', 'btn-blue' => '青色', 'btn-light-blue' => '水色', 'btn-cyan' => '明るい青(シアン)', 'btn-teal' => '緑色がかった青(ティール)', 'btn-green' => '緑色', 'btn-light-green' => '明るい緑', 'btn-lime' => 'ライム', 'btn-yellow' => '黄色', 'btn-amber' => '琥珀色(アンバー)', 'btn-orange' => 'オレンジ', 'btn-deep-orange' => 'ディープオレンジ', 'btn-brown' => '茶色', 'btn-grey' => '灰色', ]; wp_nonce_field('auto_cta_action', 'auto_cta_nonce'); ?> <div class="auto-cta"> <div class="auto-cta__preview-frame" style="margin-bottom:16px;"> <iframe id="auto-cta-preview-frame" src="<?= esc_url($preview_url); ?>" style="width:100%;border:1px solid #ddd;min-height:300px;"></iframe> </div> <hr> <p> <label>見出し</label><br> <input type="text" name="heading" value="<?= esc_attr($heading); ?>" class="widefat"> </p> <p> <label>画像とメッセージのレイアウト</label><br> <select name="layout"> <option value="cta-top-and-bottom" <?php selected($layout, 'cta-top-and-bottom'); ?>>画像・メッセージを上下に配置</option> <option value="cta-left-and-right" <?php selected($layout, 'cta-left-and-right'); ?>>画像・メッセージを左右に配置</option> <option value="cta-right-and-left" <?php selected($layout, 'cta-right-and-left'); ?>>メッセージ・画像を左右に配置</option> </select> </p> <p> <label>画像</label><br> <input type="text" name="image_url" id="auto-cta-image-url" value="<?= esc_attr($image_url); ?>" class="widefat" style="margin-bottom:5px;" placeholder="URLがここに入ります"> <button type="button" class="button" id="auto-cta-media-button">メディアから選択</button> <button type="button" class="button" id="auto-cta-media-clear">クリア</button> </p> <p> <label>ボタンテキスト</label><br> <input type="text" name="button_text" value="<?= esc_attr($button_text); ?>"> </p> <p> <label>ボタンURL</label><br> <input type="url" name="button_url" value="<?= esc_attr($button_url); ?>" class="widefat"> </p> <p> <label>ボタン色</label><br> <select name="button_color"> <?php foreach ($button_colors as $val => $label) : ?> <option value="<?= esc_attr($val); ?>" <?php selected($button_color, $val); ?>><?= esc_html($label); ?></option> <?php endforeach; ?> </select> </p> <label>表示対象カテゴリー</label> <div class="auto-cta__custom-tax-container categorydiv" id="cta-custom-tax-container" style="max-height:200px; overflow-y:auto; border:1px solid #ddd; padding:8px; background:#fdfdfd;"> <ul class="auto-cta__category-checklist categorychecklist form-no-clear" id="auto-cta-category-checklist" style="margin:0"> <?php wp_terms_checklist(0, [ 'taxonomy' => 'category', 'checked_ontop' => false, 'selected_cats' => $selected_terms, ]); ?> </ul> </div> <div class="auto-cta__hidden-cats" id="auto-cta-hidden-cats"></div> </div> <?php } endif; // 管理画面用JS・CSSを追加 add_action('admin_enqueue_scripts', function ($hook_suffix) { global $post; if ( ($hook_suffix === 'post-new.php' || $hook_suffix === 'post.php') && isset($post) && $post->post_type === 'my_auto_cta' ) { wp_enqueue_media(); wp_enqueue_script('auto-cta-admin', AUTO_CTA_URL . 'assets/js/admin.js', ['jquery', 'wp-element', 'wp-data'], '1.0.0', true); } }); - ラベルPHPを追加
以下のコードをcpt.phpに追加します。
<?php if (!defined('ABSPATH')) { exit; } // カスタム投稿タイプを追加 add_action('init', function () { register_post_type('my_auto_cta', [ 'label' => 'CTA', 'public' => false, 'show_ui' => true, 'supports' => ['title', 'editor'], 'menu_position' => 20, 'show_in_rest' => true, ]); }); // 管理画面一覧にカラムを追加 add_filter('manage_my_auto_cta_posts_columns', function ($columns) { $columns['cta_categories'] = 'カテゴリー'; $columns['button_url_status'] = 'リンク状態'; return $columns; }); // 管理画面一覧のカラムにデータを表示 add_action('manage_my_auto_cta_posts_custom_column', function ($column, $post_id) { switch ($column) { case 'cta_categories': // 表示対象カテゴリーを取得 $cats = get_post_meta($post_id, 'my_auto_cta_target_categories', true); if (empty($cats) || !is_array($cats)) { echo '—'; break; } $names = []; // IDごとにカテゴリー情報を取得 foreach ($cats as $cat_id) { $term = get_term($cat_id, 'category'); if (!$term || is_wp_error($term)) { continue; } $names[] = $term->name; } // カテゴリー名を表示 echo !empty($names) ? esc_html(implode('、', $names)) : '—'; break; case 'button_url_status': // ボタンURLを取得 $url = get_post_meta($post_id, 'my_auto_cta_button_url', true); if (empty($url)) { echo '—'; break; } // HTTPステータスを取得 $response = wp_remote_head($url, ['timeout' => 5]); if (is_wp_error($response)) { echo '<span style="color:red;">リンク切れ</span>'; break; } $status = wp_remote_retrieve_response_code($response); if ($status >= 200 && $status < 300) { echo '<span style="color:green;">OK</span>'; } else { echo '<span style="color:red;">リンク切れ</span>'; } break; } }, 10, 2); - ラベルPHPを追加
以下のコードをmatcher.phpに追加します。
<?php if (!defined('ABSPATH')) { exit; } /** * 投稿にマッチするCTA投稿IDを取得 * * @return array CTA投稿IDの配列 */ if (!function_exists('auto_cta_get_matched_cta_ids')): function auto_cta_get_matched_cta_ids() { // 現在の投稿IDを取得 $post_id = get_the_ID(); // 投稿IDが存在しない場合は処理対象外 if (!$post_id) { return []; } // 現在の投稿のカテゴリーIDを取得 $post_cats = wp_get_post_categories($post_id); // カテゴリーが存在しない場合は処理対象外 if (empty($post_cats)) { return []; } // 公開済みCTAを取得 $query = new WP_Query([ 'post_type' => 'my_auto_cta', 'posts_per_page' => -1, 'post_status' => 'publish', 'fields' => 'ids', 'meta_query' => [ [ 'key' => 'my_auto_cta_target_categories', 'compare' => 'EXISTS', ], ], ]); // CTAが存在しない場合は処理対象外 if (empty($query->posts)) { return []; } $matched = []; foreach ($query->posts as $cta_id) { // CTAの表示対象カテゴリーを取得 $cta_cats = (array) get_post_meta($cta_id, 'my_auto_cta_target_categories', true); // 対象カテゴリーが存在しない場合は処理対象外 if (empty($cta_cats)) { continue; } // 投稿カテゴリーと対象カテゴリーが一致したCTAを追加 if (array_intersect($post_cats, $cta_cats)) { $matched[] = $cta_id; } } return $matched; } endif; - ラベルPHPを追加
以下のコードをpreview.phpに追加します。
<?php if (!defined('ABSPATH')) { exit; } // Ajaxアクションを追加 add_action('wp_ajax_auto_cta_preview', function () { // 権限チェック if (!current_user_can('edit_posts')) { wp_die(); } // 投稿IDを取得 $post_id = (int) ($_GET['post_id'] ?? 0); if (!$post_id) { wp_die(); } // ヘッダーを出力 status_header(200); header('Content-Type: text/html; charset=' . get_option('blog_charset')); ?> <!doctype html> <html> <head> <meta charset="utf-8"> <!-- Cocoon 親テーマ CSS --> <link rel="stylesheet" href="<?= esc_url(get_template_directory_uri() . '/style.css'); ?>"> <!-- Cocoon 子テーマ CSS --> <link rel="stylesheet" href="<?= esc_url(get_stylesheet_directory_uri() . '/style.css'); ?>"> <!-- Font Awesome 4 --> <link rel="stylesheet" href="<?= esc_url(FONT_AWESOME_4_URL); ?>"> </head> <body style="margin:0;padding:12px;background:#fff;"> <?= auto_cta_render_from_post_id($post_id); ?> </body> </html> <?php exit; }); - ラベルPHPを追加
以下のコードをrenderer.phpに追加します。
<?php if (!defined('ABSPATH')) { exit; } /** * CTA投稿IDからCTAのHTMLを生成 * * @param int $cta_id CTA投稿ID * @return string CTA用HTML */ if (!function_exists('auto_cta_render_from_post_id')): function auto_cta_render_from_post_id($cta_id) { if (!$cta_id) { return ''; } // 本文を取得 $content = get_post_field('post_content', $cta_id); // CTAデータを取得 $data = [ 'heading' => get_post_meta($cta_id, 'my_auto_cta_heading', true), 'image_url' => get_post_meta($cta_id, 'my_auto_cta_image_url', true), 'layout' => get_post_meta($cta_id, 'my_auto_cta_layout', true), 'button_text' => get_post_meta($cta_id, 'my_auto_cta_button_text', true), 'button_url' => get_post_meta($cta_id, 'my_auto_cta_button_url', true), 'button_color' => get_post_meta($cta_id, 'my_auto_cta_button_color', true), ]; // HTMLを生成 return get_cta_tag($data, $content); } endif; - ラベルPHPを追加
以下のコードをsave.phpに追加します。
<?php if (!defined('ABSPATH')) { exit; } // カスタムフィールドを保存 add_action('save_post_my_auto_cta', function ($post_id) { // nonceチェック if (!isset($_POST['auto_cta_nonce']) || !wp_verify_nonce(wp_unslash($_POST['auto_cta_nonce']), 'auto_cta_action')) { return; } // 自動保存チェック if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) { return; } // 権限チェック if (!current_user_can('edit_post', $post_id)) { return; } // 各フィールドを保存 $fields = [ 'heading', 'image_url', 'layout', 'button_text', 'button_url', 'button_color', ]; foreach ($fields as $key) { if (!isset($_POST[$key])) { continue; } $value = sanitize_text_field(wp_unslash($_POST[$key])); update_post_meta($post_id, 'my_auto_cta_' . $key, $value); } // 表示対象カテゴリーを取得 $cats = isset($_POST['target_categories']) ? array_map('intval', (array) wp_unslash($_POST['target_categories'])) : []; // 表示対象カテゴリーを保存 update_post_meta($post_id, 'my_auto_cta_target_categories', $cats); }); - ラベルPHPを追加
以下のコードをwidget.phpに追加します。
<?php if (!defined('ABSPATH')) { exit; } /** * 自動CTAウィジェット */ class Auto_CTA_Widget extends WP_Widget { public function __construct() { parent::__construct( 'auto_cta_widget', '自動CTA(カテゴリー連動)', [ 'description' => 'カテゴリー条件に応じてCTAを自動表示します。', ] ); } /** * ウィジェットの設定フォーム * * @param array $instance 保存された値 * @return void */ public function form($instance) { $title = $instance['title'] ?? ''; ?> <p> <label for="<?= esc_attr($this->get_field_id('title')); ?>">タイトル:</label> <input class="widefat" id="<?= esc_attr($this->get_field_id('title')); ?>" name="<?= esc_attr($this->get_field_name('title')); ?>" type="text" value="<?= esc_attr($title); ?>"> </p> <p>設定不要です。ウィジェットを配置すると自動でCTAが表示されます。</p> <?php } /** * ウィジェットの設定を保存 * * @param array $new_instance 新しい値 * @param array $old_instance 古い値 * @return array 保存する値 */ public function update($new_instance, $old_instance) { $instance = []; // タイトルを保存 $instance['title'] = !empty($new_instance['title']) ? sanitize_text_field($new_instance['title']) : ''; return $instance; } /** * ウィジェットを表示 * * @param array $args ウィジェット引数 * @param array $instance 保存された値 * @return void */ public function widget($args, $instance) { // 投稿ページ以外は処理対象外 if (!is_single()) { return; } // 該当するCTAを取得 $cta_ids = auto_cta_get_matched_cta_ids(); // 該当するCTAがない場合は処理対象外 if (empty($cta_ids)) { return; } // ウィジェットを表示 echo $args['before_widget']; // ウィジェットタイトルを表示 if (!empty($instance['title'])) { echo $args['before_title']; echo esc_html($instance['title']); echo $args['after_title']; } // CTAを表示 echo '<div class="auto-cta"><div class="auto-cta__widget-wrapper" style="display:flex;flex-direction:column;gap:1em;">'; foreach ($cta_ids as $cta_id) { echo auto_cta_render_from_post_id($cta_id); } echo '</div></div>'; echo $args['after_widget']; } } // ウィジェットを追加 add_action('widgets_init', function () { register_widget('Auto_CTA_Widget'); }); - ラベルJavaScriptを追加
以下のコードをadmin.jsに追加します。
jQuery(function ($) { // DOM要素を取得 var $frame = $('#auto-cta-preview-frame'); var $list = $('#auto-cta-category-checklist'); var $hidden = $('#auto-cta-hidden-cats'); var $imageUrlInput = $('#auto-cta-image-url'); var $mediaButton = $('#auto-cta-media-button'); var $clearButton = $('#auto-cta-media-clear'); // 必須要素がない場合は終了 if (!$frame.length || !$list.length || !$hidden.length) { return; } /** * 選択カテゴリーを隠しフィールドに同期 */ function syncHidden() { $hidden.empty(); $list.find('input[type=checkbox]:checked').each(function () { $('<input>', { type: 'hidden', name: 'target_categories[]', value: this.value, }).appendTo($hidden); }); } /** * プレビューを再読み込み */ function reloadPreview() { if (!$frame.length) { return; } try { var url = new URL($frame.attr('src')); url.searchParams.set('_t', Date.now()); $frame.attr('src', url.toString()); } catch (e) { var src = $frame.attr('src'); var base = src.indexOf('&_t=') !== -1 ? src.split('&_t=')[0] : src; $frame.attr('src', base + '&_t=' + Date.now()); } } // メディアアップローダー var customUploader; // メディア選択 $mediaButton.on('click', function (e) { e.preventDefault(); if (customUploader) { customUploader.open(); return; } customUploader = wp.media({ title: 'CTA画像を選択', button: { text: '画像を使用する', }, multiple: false, }); // 画像選択 customUploader.on('select', function () { var attachment = customUploader .state() .get('selection') .first() .toJSON(); $imageUrlInput.val(attachment.url); reloadPreview(); }); customUploader.open(); }); // 画像をクリア $clearButton.on('click', function () { $imageUrlInput.val(''); reloadPreview(); }); // 初期化 syncHidden(); // カテゴリー変更を監視 $list.on('change', 'input[type=checkbox]', syncHidden); // Gutenbergの保存完了を監視 if (window.wp && wp.data && wp.data.select('core/editor')) { var wasSaving = false; wp.data.subscribe(function () { var editor = wp.data.select('core/editor'); if (!editor) { return; } var isSaving = editor.isSavingPost(); var isAutosaving = editor.isAutosavingPost(); // 保存完了を確認 if (wasSaving && !isSaving && !isAutosaving) { reloadPreview(); } wasSaving = isSaving; }); } // フォーム送信後にプレビューを更新 $('#post').on('submit', function () { setTimeout(reloadPreview, 1000); }); });
さいごに
今回紹介した方法を使えば、投稿ページにおいて、カテゴリーに応じたCTAを自動的に表示できるようになります。
従来のウィジェット単位での手動管理に比べ、設定や運用の手間を大幅に削減できる点が大きなメリットです。
また、どの投稿にどのCTAが表示されるかを簡単に把握できるため、運用効率が格段に向上します。
さらに、CTA本文や画像、ボタンの色などのカスタマイズも、カスタム投稿タイプ(cta)上で一元管理できます。
CTA本文はGutenbergエディターを用いて簡単に入力できるため、直感的な編集が可能です。
これにより、サイト全体で統一感のあるコンバージョン誘導を実現できます。

