[wp]コメント機能に5段階評価を追加した

めんどくさすぎるねこれ

完成イメージ

Amazonレビューみたいなやつ。

  • 5段階の★マークのやつ
  • 選択なし(0)もある

入力

<?php $args = array(
    'comment_field' =>  '<p class="comment-form-rate">'.'<span class="rate_wrap"><span class="input"><label><input type="radio" name="comment_rate" value="1"><span>1</span></label><label><input type="radio" name="comment_rate" value="2"><span>2</span></label><label><input type="radio" name="comment_rate" value="3"><span>3</span></label><label><input type="radio" name="comment_rate" value="4"><span>4</span></label><label><input type="radio" name="comment_rate" value="5"><span>5</span></label></span><span class="bar"></span><button type="button" id="comment_rate_reset"><span>消去</span></button></span>'.'</p>'."\n".
                        '<p class="comment-form-comment">'.'<textarea id="comment" name="comment" rows="6" aria-required="true"'.$aria_req.' placeholder="*COMMENT" /></textarea></p>'."\n",
    );
comment_form( $args ); ?>

「comment_field」で追記する。

追加したい内容だけでなく、元々あったものも書かないと消えちゃうから注意。

nameとvalueだけが重要で、上のサンプルは装飾のために組んだ内容になってる。

登録・出力

<?php 
//登録
function save_comment_rate_meta($comment_id) {
  if (isset($_POST['comment_rate'])) {
      $comment_rate = sanitize_text_field($_POST['comment_rate']);
      add_comment_meta($comment_id, 'comment_rate', $comment_rate);
  }
}
add_action('comment_post', 'save_comment_rate_meta');

//出力
function show_rate_in_comment($comment_text, $comment) {
  $comment_rate = get_comment_meta($comment->comment_ID, 'comment_rate', true);
  if ($comment_rate) {
      $comment_text .= '<p class="rate"><span class="num">'.esc_html($comment_rate).'</span><span class="bar" data-num="'.esc_html($comment_rate).'"></span></p>';
  }
  return $comment_text;
}
add_filter('comment_text', 'show_rate_in_comment', 10, 2);

入力した内容を登録するのと、投稿されたコメントを出力するところに追加する内容。

値を出すだけならシンプルだけどデザインの云々があるからこんな感じ。

管理画面上で表示・編集

<?php 
//一覧表示
function add_comment_rate_column($columns) {
  $columns['comment_rate'] = '評価';
  return $columns;
}
add_filter('manage_edit-comments_columns', 'add_comment_rate_column');
function show_comment_rate_column($column, $comment_ID) {
  if ($column === 'comment_rate') {
      $rate = get_comment_meta($comment_ID, 'comment_rate', true);

      if ($rate === '' || $rate === false) {
          echo '—';
      } elseif (is_numeric($rate) && $rate >= 1 && $rate <= 5) {
          echo str_repeat('★', intval($rate));
      } else {
          echo esc_html($rate);
      }
  }
}
add_action('manage_comments_custom_column', 'show_comment_rate_column', 10, 2);

//編集追加
function add_comment_rate_meta_field_to_admin($comment) {
  $rate = get_comment_meta($comment->comment_ID, 'comment_rate', true);
  ?>
  <p>
      <label for="comment_rate">評価(0〜5)</label><br />
      <select name="comment_rate" id="comment_rate">
            <option value="" <?php selected($rate, ''); ?>>0</option>
            <option value="1" <?php selected($rate, '1'); ?>>1</option>
            <option value="2" <?php selected($rate, '2'); ?>>2</option>
            <option value="3" <?php selected($rate, '3'); ?>>3</option>
            <option value="4" <?php selected($rate, '4'); ?>>4</option>
            <option value="5" <?php selected($rate, '5'); ?>>5</option>
        </select>
  </p>
  <?php
}
add_action('add_meta_boxes_comment', 'comment_rate_meta_box');
function comment_rate_meta_box() {
  add_meta_box('comment_rate', '評価(Rating)', 'add_comment_rate_meta_field_to_admin', 'comment', 'normal', 'high');
}
function update_comment_rate_meta_field($comment_id) {
  if (isset($_POST['comment_rate'])) {
      $rate = sanitize_text_field($_POST['comment_rate']);
      update_comment_meta($comment_id, 'comment_rate', $rate);
  }
}
add_action('edit_comment', 'update_comment_rate_meta_field');

管理画面上で装飾を頑張るのは自己満以外に意味がない。
評価は数値で登録してるから、見やすくなるように「★」に変更して出力。

編集のときは、入力フォームと違って見てくれはどうでもいいわけで、プルダウンで選択させちゃう。

フォームの装飾

やり方は何でもいいんだよね。

アプローチはどうとでもできるんだよね。

今回はradioベースで作った。

<p class="comment-form-rate">
    <span class="rate_wrap">
        <span class="input">
            <label><input type="radio" name="comment_rate" value="1"><span>1</span></label>
            <label><input type="radio" name="comment_rate" value="2"><span>2</span></label>
            <label><input type="radio" name="comment_rate" value="3"><span>3</span></label>
            <label><input type="radio" name="comment_rate" value="4"><span>4</span></label>
            <label><input type="radio" name="comment_rate" value="5"><span>5</span></label>
        </span>
        <span class="bar"></span>
        </span>
    <button type="button" id="comment_rate_reset"><span>消去</span></button>
</p>

<p>がベースだから<div>を入れると書式的にアウトだから<span>で割りまくる。

クリック部分はinput:radioで作って、ビジュは.barのところで作る。
1~5を選ぶんだけど、0にしたい(=評価しない)場合はradioの性質上非選択に戻すことができない。だから、リセットのためにbuttonをつける。

で、こう。

.comment-form-rate{
    display: flex;
    flex-direction: row;
    span.rate_wrap{
        width: calc(1em * 5 + .5em * 4);
        height: 1em;
        line-height: 1em;
        letter-spacing: .5em;
        display: block;
        position: relative;
        overflow: hidden;
        .bar{
            width: 0;
            height: 1em;
            line-height: 1em;
            display: block;
            overflow: hidden;
            position: absolute;
            top: 0;
            z-index: -1;
            &::before{
                content: "\e838 \e838 \e838 \e838 \e838";
                font-family: material icons outlined;
                position: absolute;
                top: 0;
                left: 0;
                z-index: -1;
                color: orange;
            }
        }
        &::before{
            content: "\e838 \e838 \e838 \e838 \e838";
            font-family: material icons outlined;
            position: absolute;
            top: 0;
            left: 0;
            z-index: -1;
            overflow: hidden;
            color: #ddd;
        }
        .input{
            width: 100%;
            display: block;
            display: flex;
            flex-direction: row;
            gap: .5em;
            label{
                cursor: pointer;
                input[type="radio"]{
                    display: none;
                    + span{
                        width: 1em;
                        height: 1em;
                        display: block;
                        text-indent: -9999px;
                    }
                }
            }
        }
    }
    button#comment_rate_reset{
        margin-left: 20px;
        padding: 0;
        background: none;
        border: none;
        display: none;
        height: 1em;
    }
}

★は念の為にWEBフォント(material icons)でやっておく。
画像の準備がだるかったというのもある。

文章で全部説明するとめっちゃ間延びするからコードから理解してほしい。

span.rate_wrap::beforeでベースになる☆(非選択)を並べる。
.bar::beforeで選択時の★を並べる。
☆と★を切り替えるんじゃなくて、アウトラインの☆の上に塗りつぶしの★を被せる感じでやってる。

input:radioの方に星をつけておいて:checkedで☆と★の切り替えもできるんだけど、例えば2番目を選択したら1番目の方も同様に切り替えなきゃいけないし、3番目を選択してから2番目を選択したら3番目を戻さなきゃいけないし、みたいなことを実装するとめちゃくちゃだるいものね。

.bar::beforeの幅を変更することで数値に応じた★を見せる感じですね。
画像でやっておけば楽だったのに、めっちゃ手間かけてますね。
一周回ってめんどくさいことしてますね。

$(function(){
    $('button#comment_rate_reset').on('click', function () {
        $('input[name="comment_rate"]').prop('checked', false);
        $('.comment-form-rate .rate_wrap .bar').css('width', '0');
        $('button#comment_rate_reset').fadeOut();
    })
    $('.comment-form-rate input[name="comment_rate"]').on('change', function () {
        let rateVal = $(this).val();
        let rateVal2 = Number(rateVal) - 1;
        $('.comment-form-rate .rate_wrap .bar').css('width', 'calc(1em * ' + rateVal + ' + .5em * ' + rateVal2 + ')');
        $('button#comment_rate_reset').fadeIn();
    })
})

ちょっとめんどいくらいだと思ってたけどcssめっちゃ多いし、DOMの使い方もどうかと思ったし、だいぶカロリー高めだと思う。

表示部分の装飾

手前で書いたfunctions.phpを踏まえて、こうすると

<?php
if (have_comments()) :
    echo '<ol class="comment-list">';
    wp_list_comments();
    echo '</ol>';
endif;
?>

こんな感じで出力される。

<ol class="comment-list">
    <li class="comment even thread-even depth-1" id="comment-*">
        <div id="div-comment-*" class="comment-body">
            <div class="comment-author vcard">
                <cite class="fn">…</cite> <span class="says">より:</span>
            </div>
            <div class="comment-meta commentmetadata">
                <a href="…">…</a>   <a class="comment-edit-link" href="…">(編集)</a>
            </div>
            <p>…</p>
            <p class="rate"><span class="num">3</span><span class="bar" data-num="3"></span></p>
        </div>
    </li>
    ⋮
</ol>

cssが吹っ飛んだときのために.numの中身を作っておいて、実際はフォームと同様に疑似要素を使った見せ方をしていく。
.num::beforeは☆、.bar::beforeは★。

data-numの数値によって★の切り替えをやる。

p.rate{
    width: calc(1em * 5);
    height: 1em;
    line-height: 1em;
    font-size: 16px;
    display: block;
    position: relative;
    overflow: hidden;
    span{
        width: calc(1em * 5);
        height: 1em;
        line-height: 1em;
        display: block;
        overflow: hidden;
        position: absolute;
        top: 0;
        left: 0;
        &::before{
            content: "\e838\e838\e838\e838\e838";
            font-family: material icons outlined;
            width: 100%;
            height: 1em;
            position: absolute;
            top: 0;
            left: 0;
            z-index: -1;
            overflow: hidden;
        }
        &.num{
            text-indent: -9999px;
            &::before{
                color: #ddd;
                text-indent: 0px;
            }
        }
        &.bar{
            width: 4em;
            &::before{
                color: orange;
                z-index: 1;
            }
        }
    }
}

jQueryはこう。

$(function(){
    $('ol.comment-list').find('li').each(function () {
        if ($(this).find('.rate').length) {
            let numVal = $(this).find('.bar').data('num');
            $(this).find('.bar').css('width', 'calc(1em * ' + numVal + ')');
        }
    })
})

フォームの使い回しだけど、見返せばやっぱり回りくどい気がするんだよな。
でもまあ、該当の<p>と<span>の組み合わせがなければ成立しないから、誤爆対策にはなるか。

まあそんな感じで。

webフォントで組んだ言い訳

画像を使うとレスポンシブを気にした時に、サイズ調整がだるい時がある。
丁度いい塩梅を測りかねる時がある。
想定以上のサイズが求められて画像が粗くなっても困る。

だったら文字にして、文字サイズに合わせて調整できるのがいいじゃんって。
WEBフォントなら画像と同じくどの環境でも合わせられるよねって。
★のために用意するんじゃなくて、そもそも別のところで使うために読み込んでるから役割増えてむしろいいよねって。

苦しいか。

コメント

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