Создание динамического голосования на jQuery и PHP

Вы можете получить супер крутой результат, когда комбинируете основы PHP и функциональность jQuery! В этом уроке мы будем создавать голосование на PHP и XHTML, применять некоторые эффекты jQuery Ajax, чтоб сымитировать перезагрузку страницы и конечно же добавим немного анимации.

План реализации проекта:

1. HTML
2. PHP
3.1. Introduction
4.2. poll_default()
5.3. poll_submit()
6.4. poll_return_result()
7.5. poll_ajax()
8. CSS
9. JavaScript
10.1. Introduction
11.2. formProcess()
12.3. loadResults()
13.4. animateResults()
14. HTML
Для начала, заполним раздел <head>:

<link href="style.css" rel="stylesheet" type="text/css" />
<script src="jquery.js" type="text/javascript" charset="utf-8"></script>
<script src="jquery.cookie.js" type="text/javascript" charset="utf-8"></script>
<script src="poll.js" type="text/javascript" charset="utf-8"></script>

style.css - будет содержать в себе все необходимые стили
jquery.js - сама библиотека jQuery
jquery.cookie.js - плагин, созданный Клаусом Хартлом, для управления Cookies через jQuery
poll.js - тут будет размещен скрипт, обеспечивающий динамичность голосования
Следующий шаг это создания формы для нашего голосования:

<div id="poll-container">
<h3>Голосование</h3>
<form id='poll' action="poll.php" method="post" accept-charset="utf-8">
<p>Голосуйте за ваш любимый Javascript framework:</p>
<p><input type="radio" name="poll" value="opt1" id="opt1" /><label for='opt1'> jQuery</label><br />
<input type="radio" name="poll" value="opt2" id="opt2" /><label for='opt2'> Ext JS</label><br />
<input type="radio" name="poll" value="opt3" id="opt3" /><label for='opt3'> Dojo</label><br />
<input type="radio" name="poll" value="opt4" id="opt4" /><label for='opt4'> Prototype</label><br />
<input type="radio" name="poll" value="opt5" id="opt5" /><label for='opt5'> YUI</label><br />
<input type="radio" name="poll" value="opt6" id="opt6" /><label for='opt6'> mootools</label><br /><br />
<input type="submit" value="Голосовать →" /></p>
</form>
</div>

Данная форма будет обрабатываться средствами php и сейчас и когда мы добавим функциональность jQuery. С помощью PHP и JavaScript мы будем доставать значение атрибута value тега input через определённые id. &nbps; это HTML сущность которая представляет собой пробел; &rarr; это стрелка →.

PHP
Вступление
Если JavaScript выключен PHP скрипт будет работать по следующей схеме:

Получает GET/POST запрос из формы;
Задаёт/Изымает значение Cookie;
Производит проверку ip для устранения множественного голосования;
Сохраняет голос в FlatFile db;
Возвращает результат, включенный в HTML файл;
В случае, если JavaScript включён PHP будет действовать так:

Получать GET/POST запрос от JavaScript;
Производит проверку ip для устранения множественного голосования;
Сохраняет голос в FlatFile db;
Возвращать результат в формате JSON
Для работы с текстовыми файлами мы будем использовать PHP FlatFile Package (автор Luke Plant).

Для начала нам необходимо создать массив, который будет содержать название и id, опций голосования:

<?php
$options[1] = 'jQuery';
$options[2] = 'Ext JS';
$options[3] = 'Dojo';
$options[4] = 'Prototype';
$options[5] = 'YUI';
$options[6] = 'mootools';

Пакет FlatFile использует числа для идентификации колонок, поэтому нам необходимо определить некоторые константы, чтоб по значению номеров, получать имена:

define('OPT_ID', 0);
define('OPT_TITLE', 1);
define('OPT_VOTES', 2);

Когда форма будет заполнена и отправлена на обработку, PHP необходимо знать в какой файл записывать результат и соответственно возвращать. Поэтому нам необходимо создать ещё одну константу.

define('HTML_FILE', 'index.html');

Далее нам необходимо присоединить flatfile.php и инициализировать объект базы данных:

require_once('flatfile.php');
$db = new Flatfile();

FlatFile представляет из себя обычный текстовой документ, расположенный в каталоге data:

$db->datadir = 'data/'; 
define('VOTE_DB', 'votes.txt'); 
define('IP_DB', 'ips.txt');

Если в GET/POST запросе присутствует параметр poll, это значит, что у пользователя отключён JavaScript и мы будем обрабатывать данные традиционным PHP способом. Если в GET/POST запросе присутствует параметр vote, это значит, что к нам идёт AJAX запрос. В любом другом случае мы просто возвращаем HTML_FILE:

if ($_GET['poll'] || $_POST['poll']) { 
  poll_submit(); 
} 
else if ($_GET['vote'] || $_POST['vote']) { 
  poll_ajax(); 
} 
else { 
  poll_default(); 
}
poll_default()
function poll_default() { 
  global $db; 
   
  $ip_result = $db->selectUnique(IP_DB, 0, $_SERVER['REMOTE_ADDR']); 
   
  if (!isset($_COOKIE['vote_id']) && empty($ip_result)) { 
    print file_get_contents(HTML_FILE); 
  } 
  else { 
    poll_return_results($_COOKIE['vote_id']); 
  } 
}

poll_default() напрямую обрабатывает запрос, в случае если GET/POST запросы не валидны.

Ключевое слово global делает объект $db доступным в пределах области видимости функции.

Скрипт должен проверять IP на уникальность, чтобы исключить множественное голосование. Для этого нам необходимо сделать запрос в наш текстовой файл, чтоб узнать, присутствует ли данный IP в списке.

$ip_result = $db->selectUnique(IP_DB, 0, $_SERVER['REMOTE_ADDR']);

Если Cookie отсутсвуют и ответ на запрос об IP пришёл пустой, это значит что данный пользователь ещё не голосовал поэтому мы ему отобразим HTML файл с формой. В противном случае, мы ему покажем результат:

if (!isset($_COOKIE['vote_id']) && empty($ip_result)) { 
  print file_get_contents(HTML_FILE); 
} 
else { 
  poll_return_results($_COOKIE['vote_id']); 
}
poll_submit()
function poll_submit() { 
  global $db; 
  global $options; 
   
  $id = $_GET['vote'];
  $id = str_replace("opt", '', $id); 
   
  $ip_result = $db->selectUnique(IP_DB, 0, $_SERVER['REMOTE_ADDR']); 
   
  if (!isset($_COOKIE['vote_id']) && empty($ip_result)) { 
    $row = $db->selectUnique(VOTE_DB, OPT_ID, $id); 
    if (!empty($row)) { 
      $ip[0] = $_SERVER['REMOTE_ADDR']; 
      $db->insert(IP_DB, $ip); 
   
      setcookie("vote_id", $id, time()+31556926); 
   
      $new_votes = $row[OPT_VOTES]+1; 
      $db->updateSetWhere(VOTE_DB, array(OPT_VOTES => $new_votes), new SimpleWhereClause(OPT_ID, '=', $id)); 
   
      poll_return_results($id); 
    } 
    else if ($options[$id]) { 
      $ip[0] = $_SERVER['REMOTE_ADDR']; 
      $db->insert(IP_DB, $ip); 
   
      setcookie("vote_id", $id, time()+31556926); 
   
      $new_row[OPT_ID] = $id; 
      $new_row[OPT_TITLE] = $options[$id]; 
      $new_row[OPT_VOTES] = 1; 
      $db->insert(VOTE_DB, $new_row); 
   
      poll_return_results($id); 
    } 
  } 
  else { 
    poll_return_results($id); 
  } 
}

poll_submit() принимает данные от формы, проверяет, голосовал ли пользователь, а потом обновляет БД, добавляя голос.

Этот фрагмент кода изымает значение опции ID варианта голосования и преобразовывает его в чистое значение без текста:

$id = $_GET['poll'] || $_POST['poll']; 
$id = str_replace("opt", '', $id);

Так же нам надо проверить есть ли такая опция в нашей базе (id варианта)

$row = $db->selectUnique(VOTE_DB, OPT_ID, $id);
Если такой вариант в базе присутствует (ответ не пустой), нам необходимо запустить функцию updateSetWhere(). Если нет, нам нужно её вставить - insert():

if (!empty($row)) { 
  $new_votes = $row[OPT_VOTES]+1; 
  $db->updateSetWhere(VOTE_DB, array(OPT_VOTES => $new_votes), new SimpleWhereClause(OPT_ID, '=', $id)); 
   
  poll_return_results($id); 
} 
else if ($options[$id]) { 
  $new_row[OPT_ID] = $id; 
  $new_row[OPT_TITLE] = $options[$id]; 
  $new_row[OPT_VOTES] = 1; 
  $db->insert(VOTE_DB, $new_row); 
   
  poll_return_results($id); 
}

Тем же путём нам необходимо вставить в базу IP пользователя и установить Сookie сроком на 1 год:

$ip[0] = $_SERVER['REMOTE_ADDR']; 
$db->insert(IP_DB, $ip); 
   
setcookie("vote_id", $id, time()+31556926);
poll_return_results()
function poll_return_results($id = NULL) { 
    global $db; 
   
    $html = file_get_contents(HTML_FILE); 
    $results_html = "<div id='poll-container'><div id='poll-results'><h3>Poll Results</h3>\n<dl class='graph'>\n";
   
    $rows = $db->selectWhere(VOTE_DB, 
      new SimpleWhereClause(OPT_ID, "!=", 0), -1, 
      new OrderBy(OPT_VOTES, DESCENDING, INTEGER_COMPARISON)); 
   
    foreach ($rows as $row) { 
      $total_votes = $row[OPT_VOTES]+$total_votes; 
    } 
   
    foreach ($rows as $row) { 
      $percent = round(($row[OPT_VOTES]/$total_votes)*100); 
      if (!$row[OPT_ID] == $id) { 
        $results_html .= "<dt class='bar-title'>". $row[OPT_TITLE] ."</dt><dd class='bar-container'><div id='bar". $row[OPT_ID] ."'style='width:$percent%;'> </div><strong>$percent%</strong></dd>\n"; 
      } 
      else { 
        $results_html .= "<dt class='bar-title'>". $row[OPT_TITLE] ."</dt><dd class='bar-container'><div id='bar". $row[OPT_ID] ."' style='width:$percent%;background-color:#0066cc;'> </div><strong>$percent%</strong></dd>\n"; 
      } 
    } 
   
    $results_html .= "</dl><p>Total Votes: ". $total_votes ."</p></div></div>\n"; 
   
    $results_regex = '/<div id="poll-container">(.*?)<\/div>/s'; 
    $return_html = preg_replace($results_regex, $results_html, $html); 
    print $return_html; 
}

poll_return_results() генерирует результаты опроса, записывая их в HTML файл вместо формы и возвращает пользователю.

В первую очередь давайте присвоим HTML переменной $html:

$html = file_get_contents(HTML_FILE);

Следующим файлом наклипаем начальную структуру html:

$results_html = "<div id='poll-container'><div id='poll-results'><h3>Poll Results</h3>\n<dl class='graph'>\n";

Чтобы сформировать результаты опроса необходимо выбрать все строки (варианты) из БД отсортированные по количеству голосов.

$rows = $db->selectWhere(VOTE_DB, 
  new SimpleWhereClause(OPT_ID, "!=", 0), -1, 
  new OrderBy(OPT_VOTES, DESCENDING, INTEGER_COMPARISON));

Нам так же необходимо общее количество голосов для вычисления процентов:

foreach ($rows as $row) { 
  $total_votes = $row[OPT_VOTES]+$total_votes; 
}

Следующим шагом вычисляем процентаж каждой опции голосования:

foreach ($rows as $row) { 
  $percent = round(($row[OPT_VOTES]/$total_votes)*100);

HTML в который буду записаны результаты состоит из списка (dl), стилизованного посредством CSS для создания графика.

$results_html .= "<dt class='bar-title'>". $row[OPT_TITLE] ."</dt><dd class='bar-container'><div id='bar". $row[OPT_ID] ."'style='width:$percent%;'> </div><strong>$percent%</strong></dd>\n";

Так же при переборе мы должны изменить цвет той опции за которую голосовал пользователь:

if (!$row[OPT_ID] == $id) { 
   
} 
else { 
  $results_html .= "<dt class='bar-title'>". $row[OPT_TITLE] ."</dt><dd class='bar-container'><div id='bar". $row[OPT_ID] ."' style='width:$percent%;background-color:#0066cc;'> </div><strong>$percent%</strong></dd>\n"; 
}

Тут мы выводим общее число голосов и закрываем наш HTML:

$results_html .= "</dl><p>Total Votes: ". $total_votes ."</p></div></div>\n";

А вот это регулярное выражение, которое находит

с id poll-container:

$results_regex = '/<div id="poll-container">(.*?)<\/div>/s';

В этой функции нам осталось заменить HTML самого опроса, на HTML результатов с помощью регулярного выражения:

$return_html = preg_replace($results_regex, $results_html, $html); 
print $return_html;
poll_ajax()
function poll_ajax() { 
  global $db; 
  global $options; 
   
  $id = $_GET['vote'] || $_POST['vote']; 
   
  $ip_result = $db->selectUnique(IP_DB, 0, $_SERVER['REMOTE_ADDR']); 
   
  if (empty($ip_result)) { 
    $ip[0] = $_SERVER['REMOTE_ADDR']; 
    $db->insert(IP_DB, $ip); 
   
    if ($id != 'none') { 
      $row = $db->selectUnique(VOTE_DB, OPT_ID, $id); 
      if (!empty($row)) { 
        $new_votes = $row[OPT_VOTES]+1; 
   
        $db->updateSetWhere(VOTE_DB, array(OPT_VOTES => $new_votes), new SimpleWhereClause(OPT_ID, '=', $id)); 
      } 
      else if ($options[$id]) { 
        $new_row[OPT_ID] = $id; 
        $new_row[OPT_TITLE] = $options[$id]; 
        $new_row[OPT_VOTES] = 1; 
        $db->insert(VOTE_DB, $new_row); 
      } 
    } 
  } 
   
  $rows = $db->selectWhere(VOTE_DB, new SimpleWhereClause(OPT_ID, "!=", 0), -1, new OrderBy(OPT_VOTES, DESCENDING, INTEGER_COMPARISON)); 
  print json_encode($rows); 
}

poll_ajax() принимает запрос от JavaScript, добавляет голоса в БД и возвращает результат в формате JSON.

В этом коде по сравнению с poll_submit() присутствуют некоторые изменения. Первое это проверка на то, что JavaScript делает запрос за результатами и в этом случае голоса не подсчитываются:

if ($id != 'none')

Следующие строки кода извлекают всю инфу из БД и возвращают её в формате JSON:

$rows = $db->selectWhere(VOTE_DB, new SimpleWhereClause(OPT_ID, "!=", 0), -1, new OrderBy(OPT_VOTES, DESCENDING, INTEGER_COMPARISON)); 
print json_encode($rows);

CSS
.graph { 
  width: 250px; 
  position: relative; 
  rightright: 30px; 
} 
.bar-title { 
  position: relative; 
  float: left; 
  width: 104px; 
  line-height: 20px; 
  margin-right: 17px; 
  font-weight: bold; 
  text-align: rightright; 
} 
.bar-container { 
  position: relative; 
  float: left; 
  width: 110px; 
  height: 10px; 
  margin: 0px 0px 15px; 
} 
   
.bar-container div { 
  background-color:#cc4400; 
  height: 20px; 
} 
.bar-container strong { 
  position: absolute; 
  rightright: -32px; 
  top: 0px; 
  overflow: hidden; 
} 
#poll-results p { 
  text-align: center; 
}

CSS стилизует результаты, возвращённые PHP или JavaScript-ом:

.graph стилизует контейнер для прямоугольников, заголовков и процентов. Опция width будет различной для разных сайтов;
.bar-title заголовок для прямоугольного графика;
.bar-container оформляет индивидуальный прямоугольник и контейнер процентов;
.bar-container div оформляет див, в котором содержится контент. Значение width задаётся из PHP или Javascript;
.bar-container strong оформляет процентные данные;
#poll-results p оформляет общее число голосов;
Javascript
Вступление
JavaScript будет использоваться для отправки информации из формы, для отправления нового голоса через AJAX запрос и анимирования результатов.

Для начала создадим некоторые глобальные переменные. Первые три такие же какие у нас были в PHP. В votedID будет храниться id опции которую выбрал пользователь.

var OPT_ID = 0; 
var OPT_TITLE = 1; 
var OPT_VOTES = 2;


var votedID;
Теперь нужно заставить код запуститься после загрузки страницы:

$(document).ready(function(){
Внутри данной функции мы устанавливаем обработчик для кнопки, на которую пользователи буду нажимать, чтоб проголосовать. Когда кнопка будет нажата в бой вступит функция formProcess:
$("#poll").submit(formProcess);
Так же нам надо проверить какие данные сейчас отображеные на странице. Если это не сама форма голосования, то нам необходимо анимировать результаты:

if ($("#poll-results").length > 0 ) { 
    animateResults(); 
}

Если Cookie существуют, то мы должны отобразить результаты, т.к. пользователь уже голосовал. Для этого нам необходимо извлечь значение из Cookie и результаты средсвом PHP. Следующий шаг отправка извлечённых данные в функцию loadResults():

if ($.cookie('vote_id')) 
    $("#poll-container").empty(); 
    votedID = $.cookie('vote_id'); 
    $.getJSON("poll.php?vote=none",loadResults); 
}
formProcess()
function formProcess(event){ 
  event.preventDefault(); 
   
  var id = $("input[@name='poll']:checked").attr("value"); 
  id = id.replace("opt",''); 
   
  $("#poll-container").fadeOut("slow",function(){ 
    $(this).empty(); 
   
    votedID = id; 
    $.getJSON("poll.php?vote="+id,loadResults); 
   
    $.cookie('vote_id', id, {expires: 365}); 
    }); 
}

formProcess() вызывается, когда форма заполнена. Это позволяет определить каким способом была отправлена форма: PHP или JavaScript. Далее происходит проверка/установка Сookies, запускается Ajax извлечение данных для преобразования результатов в HTML.

Для начала нам необходимо предотвратить отправку формы по умолчанию: event.preventDefault();

Далее мы извлекаем ID выбранной опции:

var id = $("input[@name='poll']:checked").attr("value"); 
id = id.replace("opt",'');

input[@name='poll']:checked это селектор jQuery который выбирает <input> у которого есть атрибут name равный значению 'poll', причём данный элемент должен быть выбран. attr("value") извлекает значение объекта, который в нашем случае является optn, где n это ID опции.

Теперь, когда у нас есть ID, мы можем его обработать. Для этого мы скроем форму голосования, и настроем анонимную функцию для возврата ответа, которая запустится, когда форма голосования исчезнет полностью.

$("#poll-container").fadeOut("slow",function(){

После того как форма исчезнет, мы её удалим из DOM с помощью empty():

$(this).empty();

В данном случае $(this) это специальная переменная, которая представляет собой объект, который изчезал со страницы (в нашем случае форма).

В jQuery так же присутствует немало специальных функций, таких к примеру как $.getJSON(), которая принимает GET запрос от JSON объекта. Когда у нас будет объект мы вызываем функцию loadResults():

$.getJSON("poll.php?vote="+id,loadResults);

Нам осталось только записать значение в Cookie:

$.cookie('vote_id', id, {expires: 365});
loadResults()
function loadResults(data) { 
  var total_votes = 0; 
  var percent; 
   
  for (id in data) { 
    total_votes = total_votes+parseInt(data[id][OPT_VOTES]); 
  } 
   
  var results_html = "<div id='poll-results'><h3>Poll Results</h3>\n<dl class='graph'>\n"; 
  for (id in data) { 
    percent = Math.round((parseInt(data[id][OPT_VOTES])/parseInt(total_votes))*100); 
    if (data[id][OPT_ID] !== votedID) { 
      results_html = results_html+"<dt class='bar-title'>"+data[id][OPT_TITLE]+"</dt><dd class='bar-container'><div id='bar"+data[id][OPT_ID]+"'style='width:0%;'> </div><strong>"+percent+"%</strong></dd>\n"; 
    } else { 
      results_html = results_html+"<dt class='bar-title'>"+data[id][OPT_TITLE]+"</dt><dd class='bar-container'><div id='bar"+data[id][OPT_ID]+"'style='width:0%;background-color:#0066cc;'> </div><strong>"+percent+"%</strong></dd>\n"; 
    } 
  } 
   
  results_html = results_html+"</dl><p>Total Votes: "+total_votes+"</p></div>\n"; 
   
  $("#poll-container").append(results_html).fadeIn("slow",function(){ 
    animateResults();}); 
}

loadResults() вызывается из $.getJSON() и возвращает JSON объект, в котором содержутся данные из БД. Эта функция работает практически так же как poll_return_results(). Первое отличие в том, что мы устанавливаем width прямоугольников в 0% потому как собираемся их анимировать. Другое отличие в том, что мы используем функцию jQuery append() вместо регулярных выражений для вывода результата. После того, как результаты появятся на странице мы вызываем функцию animateResults().

animateResults()

function animateResults(){ 
  $("#poll-results div").each(function(){ 
      var percentage = $(this).next().text(); 
      $(this).css({width: "0%"}).animate({ 
                width: percentage}, 'slow'); 
  }); 
}

animateResults() последовательно проходит по всем прямоугольниками и анимирует изменение ширины (width).
each() это функция jQuery которая последовательно проходит по всем элементам попадающим под селектор:

$("#poll-results div").each(function(){

Для начала мы зададим значения тексту, который находится после прямоугольника заключённого в тег <strong>, содержащий проценты.

var percentage = $(this).next().text();

Затем мы выставляем значение 0% для width и запускаем анимацию:

$(this).css({width: "0%"}).animate({ 
  width: percentage}, 'slow');

Теги:
results, container, VOTES, votes, width, return, strong, class, percent, options, необходимо, total, function, input, label, будет, empty, TITLE, value, запрос, jQuery, JavaScript, голосования, loadResults, result, submit, REMOTE, результаты, SERVER, insert, style, случае, title, форма, SimpleWhereClause
Добавлено: 18 Апреля 2018 08:15:08 Добавил: Андрей Ковальчук Нравится 0
Добавить
Комментарии:
Нету комментариев для вывода...