はじめに
以下の質問に回答します。
プラグイン「Post Reaction」などを使用せずに、以前紹介して「いいね」機能を拡張し、投稿本文の下、コメントに「リアクション」ボタンを追加します。
従来の「いいね」機能は1種類の反応しか扱えませんでしたが、ここでは複数のリアクション(例:いいね・参考になった・すごい など)を扱えるようにします。
この記事では、この方法について説明します。
完成イメージ

実装手順
以下の手順で実装します。
- ラベルPHPを追加(共通)
以下のコードをfunctions.phpに追加します。
/** * Ajaxコールバック関数 * * @return void */ if (!function_exists('reactions_callback')): function reactions_callback() { check_ajax_referer('reactions_action', 'reactions_nonce'); // POSTパラメータを取得 $type = $_POST['type'] ?? 'post'; $id = intval($_POST['id'] ?? 0); $reaction = $_POST['reaction'] ?? ''; $prev = $_POST['prev_reaction'] ?? ''; $key = 'my_' . $type . '_reactions'; $type = sanitize_text_field($type); $reaction = sanitize_text_field($reaction); $prev = sanitize_text_field($prev); // カスタムフィールドに保存されているデータを取得 $stored_raw = ($type === 'comment') ? get_comment_meta($id, $key, true) : get_post_meta($id, $key, true); $stored = json_decode((string) $stored_raw, true); if (!is_array($stored)) { $stored = []; } // 前回のリアクションを減算 if ($prev && isset($stored[$prev]) && $stored[$prev] > 0) { $stored[$prev]--; } // 新しいリアクションを加算 if ($reaction) { if (!isset($stored[$reaction])) { $stored[$reaction] = 0; } $stored[$reaction]++; } // カスタムフィールドを更新 $val = wp_json_encode($stored); if ($type === 'comment') { update_comment_meta($id, $key, $val); } else { update_post_meta($id, $key, $val); } // 更新情報をJSON形式で返却 wp_send_json_success($stored); } endif; // フロント用JSとライブラリを追加 add_action('wp_enqueue_scripts', function () { wp_enqueue_script('jquery-cookie', 'https://cdnjs.cloudflare.com/ajax/libs/jquery-cookie/1.4.1/jquery.cookie.min.js', ['jquery'], '1.4.1', true); $ajax_url = esc_url(admin_url('admin-ajax.php')); $nonce = wp_create_nonce('reactions_action'); ob_start(); ?> jQuery(function ($) { /** * クッキーから保存済みのIDリストを取得 * * @param {string} type 対象の種別 * @return {Object} 保存済みIDのオブジェクト */ function getStoredIds(type) { var data = $.cookie('my_' + type + '_reactions'); return data ? JSON.parse(data) : {}; } /** * ページ内の全ボタンの状態をクッキーと同期 * * @return {void} */ function syncAllButtonStates() { $('.reactions').each(function () { var $con = $(this), type = $con.data('type'), id = String($con.data('id')), stored = getStoredIds(type), active = stored[id]; $con.find('.reactions__button').removeClass('active'); if (active) { $con.find('.reactions__button[data-reaction="' + active + '"]').addClass('active'); } }); } // ページ読み込み時に実行 syncAllButtonStates(); $(document).on('click', '.reactions__button', function (e) { e.preventDefault(); var $btn = $(this), $con = $btn.closest('.reactions'), type = $con.data('type'), id = String($con.data('id')), reaction = $btn.data('reaction'), stored = getStoredIds(type), prev = stored[id] || '', mode = (prev === reaction) ? 'remove' : 'add'; // IDリストを更新 if (mode === 'remove') { delete stored[id]; reaction = ''; } else { stored[id] = reaction; } // クッキーに保存(7日間有効) $.cookie('my_' + type + '_reactions', JSON.stringify(stored), { expires: 7, path: '/' }); // Ajaxでサーバーに送信して情報を更新 $.post('<?= $ajax_url; ?>', { action: 'reactions', type: type, id: id, reaction: reaction, prev_reaction: prev, reactions_nonce: '<?= $nonce; ?>' }, function (res) { if (res.success) { // カウント数を更新 $.each(res.data, function (k, v) { $con.find('.reactions__button[data-reaction="' + k + '"] .reactions__count').text(v); }); syncAllButtonStates(); } }, 'json'); }); }); <?php wp_add_inline_script('jquery-cookie', ob_get_clean()); }); // Ajaxアクションを追加 add_action('wp_ajax_reactions', 'reactions_callback'); add_action('wp_ajax_nopriv_reactions', 'reactions_callback'); /** * リアクションボタンを表示 * * @param int|null $post_id 対象のID * @param string $type 対象の種別 * @return void */ if (!function_exists('render_reactions')): function render_reactions($post_id = null, $type = 'post') { if (!$post_id) { $post_id = get_the_ID(); } $reactions = [ 'like' => ['icon' => '<i class="far fa-grin-beam-sweat"></i>', 'label' => '汗'], 'smile' => ['icon' => '<i class="far fa-smile"></i>', 'label' => 'スマイル'], 'wow' => ['icon' => '<i class="far fa-grin-squint"></i>', 'label' => 'すごい'], 'heart' => ['icon' => '<i class="far fa-kiss-beam"></i>', 'label' => 'ラブ'], ]; $key = 'my_' . $type . '_reactions'; $stored_raw = ($type === 'comment') ? get_comment_meta($post_id, $key, true) : get_post_meta($post_id, $key, true); $stored = json_decode((string) $stored_raw, true); if (!is_array($stored)) { $stored = []; } $output = '<div class="reactions" data-id="' . esc_attr($post_id) . '" data-type="' . esc_attr($type) . '">'; foreach ($reactions as $rk => $rv) { $count = intval($stored[$rk] ?? 0); $output .= '<span class="reactions__button" data-reaction="' . esc_attr($rk) . '" title="' . esc_attr($rv['label']) . '">'; $output .= '<span class="reactions__icon">' . $rv['icon'] . '</span>'; $output .= '<span class="reactions__count">' . $count . '</span>'; $output .= '</span>'; } $output .= '</div>'; echo $output; } endif; - ラベルPHPを追加(投稿本文の下)
以下のコードをfunctions.phpに追加します。
add_action('singular_entry_content_after', function () { if (is_single()) { render_reactions(); } }); - ラベルPHPを追加(コメント)
以下のコードをfunctions.phpに追加します。
add_filter('comment_text', function ($text, $comment) { if (is_admin()) { return $text; } ob_start(); render_reactions($comment->comment_ID, 'comment'); return $text . ob_get_clean(); }, 10, 2); - ラベルCSSを追加
以下のコードをstyle.cssに追加します。
.reactions { display: flex; gap: 10px; } .reactions__button { cursor: pointer; } .reactions__icon { color: #999; margin-right: 3px; } .reactions__button.active .reactions__icon { color: #e589a2; }
補足
$reactionsは、各リアクションに対応するアイコンとラベルを定義しています。
リアクションのカウントは、カスタムフィールドmy_post_reactions(投稿用)またはmy_comment_reactions(コメント用)に、以下のようなJSON 形式で保存されます。
{"smile":3,"sad":1,"wow":2,"angry":0}
従来の「いいねボタン」機能では、カスタムフィールド post_likeに1種類のリアクション(いいね)のカウント数のみを保存していました。
本コードでは、1つのリアクションだけを使用すれば、既存の「いいねボタン」と同じ仕組みとして利用することも可能です。
さいごに
今回紹介した方法を使えば、「リアクション」機能を追加できます。
プラグインを使わずカスタマイズできるため、自分のサイトに合わせたデザインや機能の調整が可能です。
さらに、従来の「いいねボタン」機能の代わりとしても利用でき、選択したリアクションはカスタムフィールドにJSON形式で保存されるため、柔軟に集計や表示をカスタマイズできます。


