[WordPress] Ajaxを使って投票システム作成(1つにつき1回投票可)
投票対象の商品が複数あって、投票者は、 1 つの商品に対し 1 人 1 回ずつ投票できる機能を WordPress のサイトに作った。 ( 1 人 1 回ではない。例えば商品が 10 個あったら、1 人あたり最大で 10 回投票が可能)
仕様
- 投票済みかどうかの情報はクッキーに保存
- 投票済みの商品は「投票」ボタンが無効となる
- 投票期間は 2 週間
- クッキーは、商品の数だけ存在する(商品 1 にはクッキー名 PD1 、商品 2 には PD2…)
- サーバー側で、ランダムな数値を数十個もつ配列(シークレットリスト)用意しておき、クッキーの値とシークレットリストを比較して、一致していれば投票済み、不一致なら未投票と判断する
- 投票画面は、WordPress に導入しているテーマのカスタマイザーで作成
- JavaScript でのクッキーの操作には、jQuery プラグイン「 jquery.cookie.js 」を使用
投票ページロード時
- クッキーが存在するかチェック。クッキーが存在しなければ、商品の数だけクッキーを作成し、ランダムな数値を値にセット
- クッキーの数(=商品の数)だけ、投票済みかどうかのチェックを行う(クッキーの数だけ LOOP させる。これ以降の処理は LOOP 内で行われる)
- クッキーの値を Ajax でサーバーに送り、シークレットリストと比較し、比較結果を返す
一致の場合、「投票」ボタン無効
不一致の場合、「投票」ボタン有効 - Ajax で投票数を取得し、画面に表示する
「投票」ボタンクリック時
- ボタンから、クリックされた商品の ID を取得
- 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 )をしている。