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) );