[WordPress] Ajaxを使って投票システム作成(1つにつき1回投票可)

WordPressAjax

投票対象の商品が複数あって、投票者は、 1 つの商品に対し 1 人 1 回ずつ投票できる機能を WordPress のサイトに作った。 ( 1 人 1 回ではない。例えば商品が 10 個あったら、1 人あたり最大で 10 回投票が可能)

仕様

  • 投票済みかどうかの情報はクッキーに保存
  • 投票済みの商品は「投票」ボタンが無効となる
  • 投票期間は 2 週間
  • クッキーは、商品の数だけ存在する(商品 1 にはクッキー名 PD1 、商品 2 には PD2…)
  • サーバー側で、ランダムな数値を数十個もつ配列(シークレットリスト)用意しておき、クッキーの値とシークレットリストを比較して、一致していれば投票済み、不一致なら未投票と判断する
  • 投票画面は、WordPress に導入しているテーマのカスタマイザーで作成
  • JavaScript でのクッキーの操作には、jQuery プラグイン「 jquery.cookie.js 」を使用

投票ページロード時

  1. クッキーが存在するかチェック。クッキーが存在しなければ、商品の数だけクッキーを作成し、ランダムな数値を値にセット
  2. クッキーの数(=商品の数)だけ、投票済みかどうかのチェックを行う(クッキーの数だけ LOOP させる。これ以降の処理は LOOP 内で行われる)
  3. クッキーの値を Ajax でサーバーに送り、シークレットリストと比較し、比較結果を返す
    一致の場合、「投票」ボタン無効
    不一致の場合、「投票」ボタン有効
  4. Ajax で投票数を取得し、画面に表示する

「投票」ボタンクリック時

  1. ボタンから、クリックされた商品の ID を取得
  2. Ajax で ID を送信し、新しい投票数を取得
    <サーバー側>
    • DB の投票数を +1 で更新
    • シークレットリストからランダムに選ばれた数値を、投票された商品のクッキーの値にセット
    • 投票数を取得して画面に返す
    <画面側>
    • 投票数を表示
    • クリックされた商品の「投票」ボタンを無効化する

コード

<実装に必要なファイル>
  • functions.php
  • vote.js (新規作成)
  • 投票画面( WP の固定ページ。今回ここはやらない)

PHP と JavaScript の両方から、クッキーのセットをしているが、異なるファイル場所からセットするため、クッキーの保存はドメイン配下に指定するか、保存する場所を同じにしておく。そうでないと、同じ名前のクッキーが異なる場所に保存され、比較ができなくなる。

functions.php


// シークレットリスト( 20020001 以上の整数)
$secret_list = array(
    100184397 => true,
    20029426 => true,
    30035521 => true,
    40047892 => true,
    50051739 => true,
    600633950 => true,
    70072405 => true,
    800872671 => true,
    900934554 => true,
    187105496 => true,
    201172345 => true,
    30123286 => true,
    401343526 => true,
    50140679 => true,
    601564329 => true
);
// Ajax を使えるようにする・「 jquery.cookie.js 」と自作 js の読み込み
function my_enqueue() {
	if ( is_page( 'contest-vote' ) ) {
		wp_enqueue_script('jquery.cookie', 'https://cdnjs.cloudflare.com/ajax/libs/jquery-cookie/1.4.1/jquery.cookie.min.js', array('jquery'), NULL, true);
		wp_enqueue_script( 'ajax-script', get_theme_file_uri( '/js/customjs/vote.js' ), array('jquery'), null, true );
		// Ajax を使えるようにする
		wp_localize_script( 'ajax-script', 'admin_url',array( 'ajax_url' => admin_url( 'admin-ajax.php') ) );
	}
}
add_action( 'wp_enqueue_scripts', 'my_enqueue' );
// 投票ページロード時の処理
function do_init() {
	global $secret_list;
	$z=filter_input(INPUT_POST, 'z', FILTER_VALIDATE_INT);
	header('Content-type: text/plain; charset= UTF-8');
	if( isset( $secret_list[$z] ) ) {
		// 投票不可
	    echo 0;
	} else {
		// 投票可能
	    echo 1;
	}
	wp_die();
}
add_action( 'wp_ajax_do_init', 'do_init' );
add_action( 'wp_ajax_nopriv_do_init', 'do_init' );
// 投票数取得
function get_votecnt() {
	global $wpdb;
	$list = $wpdb->get_results("SELECT id, votecnt FROM table",ARRAY_A);
	header('Content-Type: application/json; charset=utf-8');
	echo json_encode($list);
	wp_die();
}
add_action( 'wp_ajax_get_votecnt', 'get_votecnt' );
add_action( 'wp_ajax_nopriv_get_votecnt', 'get_votecnt' );
//「投票」ボタンクリック時、DB 投票数更新・クッキーセット
function do_vote() {
	global $wpdb;
	$id = filter_input(INPUT_POST, 'sid', FILTER_VALIDATE_INT);
	$wpdb->query( $wpdb->prepare( "UPDATE table SET votecnt=votecnt+1 WHERE id=%d", array($id) ) );
	// SecretList にある値をクッキーにセット
	global $secret_list;
	$ck = array_rand($secret_list, 1);
	$cname='PD'.$id;
	// 有効期限 : 現在から2週間後 (14日、24時間、60分、60秒), クッキーはドメイン配下に保存
	setcookie($cname, $ck, time() + 60 * 60 * 24 * 14, '/');
	// votecnt取得
	$votecnt = $wpdb->get_var( $wpdb->prepare( "SELECT votecnt FROM table WHERE id=%d", array($id) ) );
	header('Content-type: text/plain; charset= UTF-8');
	echo $votecnt;
	wp_die();
}
add_action( 'wp_ajax_do_vote', 'do_vote' );
add_action( 'wp_ajax_nopriv_do_vote', 'do_vote' );

シークレットリスト と、ページロード時にクッキーが存在しなければ、セットするランダムな値は、それぞれ被らないような数値の範囲を設定した。(見た目に違いが出ないように、桁数揃えるとかする。)

vote.js

テーマ/jsフォルダ内に新規作成した。
/パス/テーマ/js/customjs/vote.js


jQuery(document).ready(function($) {
  var i=1;
  var last = 3; // 商品の数 (例えば、商品が 3 つ)
  for (i; i<=last; i++) {
    var key = 'PD'+i;
    console.log(key);
    if($.cookie(key)){
      // PDx cookie 存在する場合
      var z = $.cookie(key);
      console.log("exists : "+$.cookie(key));
    } else {
      // PDx cookie が存在しない場合
      // 乱数の生成( 10000000 〜 20020000 の数値)
      var z = Math.floor(Math.random() * (20020000 - 10000000 + 1)) + 10000000;
      // cookieの有効範囲はドメイン全体
      $.cookie(key, z, { path: "/" });
      console.log("No PDx cookie : "+$.cookie(key));
    }
    $.ajax({
      type: "POST",
      url: admin_url.ajax_url,
      data:{'action' : 'do_init', "z" : z}
    }).done(function(data){
      console.log("data:"+data);
      if(data==0){
        // ボタン非表示
        console.log("disabled");
        //$('#'+i).prop('disabled', true);
      }
    }).fail(function(XMLHttpRequest, textStatus, error){
          console.log(error);
          console.log(XMLHttpRequest.responseText);
    });
  }
  // 投票数取得
  $.ajax({
    type: "POST",
    url: admin_url.ajax_url,
    data:{'action' : 'get_votecnt'},
    dataType:'json'
  }).done(function(data){
    $.each(data, function(index, value) {
      var j = index+1;
      var votespan = j+'-votecnt';
      // 投票数表示用のspanタグに投票数セット
      $('#'+votespan).text(value.votecnt);
    });
  }).fail(function(XMLHttpRequest, textStatus, error){
        console.log(error);
        console.log(XMLHttpRequest.responseText);
  });
});
// onClick
jQuery(function($) {
  $(document).on("click",".votebtn", function(event){
    // btnID取得
    var btnid = $(this).prop('id');
    // ボタン非表示
    $(this).prop('disabled', true);
    $.ajax({
      type: "POST",
      url: admin_url.ajax_url,
      data:{'action' : 'do_vote',"sid" : btnid}
    }).done(function(cnt){
      // 投票数セット
      var votespan = btnid+'-votecnt';
      $('#'+votespan).text(cnt);
      console.log('投票数:'+cnt);
    }).fail(function(XMLHttpRequest, textStatus, error){
          console.log(error);
          console.log(XMLHttpRequest.responseText);
    });
  });
});

JavaScript で乱数を生成する方法は、

Math.floor(Math.random() * (max - min)) + min
min 以上 max 未満の整数値がランダムに返ってくる。
これだと、 max 未満なので、 (max - min + 1) (プラス 1 )をしている。

Posted by Agopeanuts