Вы можете получить супер крутой результат, когда комбинируете основы 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 сущность которая представляет собой пробел; → это стрелка →.
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');