jqueryのclick functionでopacityを変更し、更にformのvalueにidを入れて、ボタンが押されたら、MySQLにIDを格納します。そして、Mysqlからsql文でデータを読み込んで、集計結果を表示させます。また、IPアドレス、ユーザーエージェント、データで、複数投票を制限しています。
-reference
bindValue:Binds a value to a corresponding named or question mark placeholder in the SQL statement that was used to prepare the statement.
SQL INSERT INTO Statement:INSERT INTO table_name VALUES (value1,value2,value3,…);
SQLデータ取得:SELECT col_name1, col_name2, … FROM db_name.tbl_name;
SQL GROUP BY Statement:The GROUP BY statement is used in conjunction with the aggregate functions to group the result-set by one or more columns.
user agent:is software (a software agent) that is acting on behalf of a user.
index.php
<?php
require_once(__DIR__ . '/config.php');
require_once(__DIR__ . '/Poll.php');
try {
$poll = new \MyApp\Poll();
} catch (Exception $e){
echo $e->getMessage();
exit;
}
if ($_SERVER['REQUEST_METHOD'] === 'POST'){
$poll->post();
}
$err = $poll->getError();
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Poll</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="styles.css">
</head>
<body>
<?php if (isset($err)) : ?>
<div class="error"><?= h($err); ?></div>
<?php endif; ?>
<h1>Which do you like best?</h1>
<form action="" method="post">
<div class="row">
<div class="box" id="box_0" data-id="0"></div>
<div class="box" id="box_1" data-id="1"></div>
<div class="box" id="box_2" data-id="2"></div>
<input type="hidden" id="answer" name="answer" value="">
<input type="hidden" name="token" value="<?= h($_SESSION['token']); ?>">
</div>
<div id="btn">Vote and See Results</div>
</form>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script>
$(function(){
'use strict';
$('.box').on('click', function(){
$('.box').removeClass('selected');
$(this).addClass('selected');
$('#answer').val($(this).data('id'));
});
$('#btn').on('click', function(){
if ($('#answer').val() === ''){
alert('Choose one!');
} else {
$('form').submit();
}
});
$('.error').fadeOut(3000);
});
</script>
</body>
</html>
Poll.php
<?php
namespace MyApp;
class Poll{
private $_db;
public function __construct(){
$this->_connectDB();
$this->_createToken();
}
private function _createToken(){
if(!isset($_SESSION['token'])){
$_SESSION['token'] = bin2hex(openssl_random_pseudo_bytes(16));
}
}
private function _validateToken(){
if(
!isset($_SESSION['token']) ||
!isset($_POST['token']) ||
$_SESSION['token'] !== $_POST['token']
){
throw new \Exception('invalid token!');
}
}
public function post(){
try {
$this->_validateToken();
$this->_validateAnswer();
$this->_save();
//redirect to result.php
header('Location: http://' . $_SERVER['HTTP_HOST'] . '/result.php');
} catch(\Exception $e){
// set error
$_SESSION['err'] = $e->getMessage();
// ridirect to index.php
header('Location: http://' . $_SERVER['HTTP_HOST']);
}
exit;
}
public function getResults(){
$data = array_fill(0, 3, 0);
$sql = "select answer, count(id) as c from answers group by answer";
foreach ($this->_db->query($sql) as $row){
$data[$row['answer']] = (int)$row['c'];
}
return $data;
}
public function getError(){
$err = null;
if (isset($_SESSION['err'])){
$err = $_SESSION['err'];
unset($_SESSION['err']);
}
return $err;
}
private function _validateAnswer(){
// var_dump($_POST);
// exit;
if (
!isset($_POST['answer']) ||
!in_array($_POST['answer'], [0, 1, 2])
){
throw new \Exception('invalid answer!');
}
}
private function _save(){
$sql = "insert into answers
(answer, created, remote_addr, user_agent, answer_date)
values (:answer, now(), :remote_addr, :user_agent, now())";
$stmt = $this->_db->prepare($sql);
$stmt->bindValue(':answer', (int)$_POST['answer'], \PDO::PARAM_INT);
$stmt->bindValue(':remote_addr', $_SERVER['REMOTE_ADDR'], \PDO::PARAM_STR);
$stmt->bindValue(':user_agent', $_SERVER['HTTP_USER_AGENT'], \PDO::PARAM_STR);
try {
$stmt->execute();
} catch(\PDOException $e){
throw new \Exception('No more vote for today!');
}
// exit;
}
private function _connectDB(){
try {
$this->_db = new \PDO(DSN, DB_USERNAME, DB_PASSWORD);
$this->_db->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
} catch(\PDOException $e){
throw new \Exception('Failed to connect DB');
}
}
}
result.php
<?php
require_once(__DIR__ . '/config.php');
require_once(__DIR__ . '/Poll.php');
try {
$poll = new \MyApp\Poll();
} catch (Exception $e){
echo $e->getMessage();
exit;
}
$results = $poll->getResults();
// var_dump($results);
// exit;
// $results = [
// 0 => 12,
// 1 => 32,
// 2 => 44
// ];
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Poll Result</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="styles.css">
</head>
<body>
<h1>Result ...</h1>
<div class="row">
<?php for($i = 0; $i < 3; $i++) : ?>
<div class="box" id="box_<?= h($i); ?>"><?= h($results[$i]); ?></div>
<?php endfor; ?>
</div>
<a href="/"><div id="btn">Go Back</div></a>
</body>
</html>
styles.css
body {
font-size: 16px;
font-family: Arial, sans-serif;
text-align: center;
margin: 0;
padding: 0;
}
h1 {
font-size: 22px;
margin: 30px 0;
}
.row {
margin-bottom: 15px;
}
.box {
width: 150px;
height: 150px;
cursor: pointer;
display: inline-block;
opacity: 0.5;
color: #fff;
font-size: 48px;
font-weight: bold;
line-height: 150px;
}
.box + .box {
margin-left: 10px;
}
#box_0 { background: url('photo_0.jpg'); }
#box_1 { background: url('photo_1.jpg'); }
#box_2 { background: url('photo_2.jpg'); }
.selected {
opacity: 1.0;
}
#btn {
display: inline-block;
width: 150px;
padding: 7px;
font-size: 14px;
cursor: pointer;
border-radius: 5px;
background: #00aaff;
box-shadow: 0 4px 0 #0088cc;
color: #fff;
}
#btn:hover{
opacity: 0.8;
}
.error{
background: orange;
padding: 7px;
color: #fff;
}
mySQL
create database poll_php;
grant all on poll_php.* to dbuser@localhost identified by 'xxxx';
use poll_php
create table answers (
id int not null auto_increment primary key,
answer int not null,
created datetime,
remote_addr varchar(15),
user_agent varchar(255),
answer_date date,
unique unique_answer(remote_addr, user_agent, answer_date)
);