Виват, дорогие читатели ! Сегодня, в этот ничем не знаменательный день, а может быть совершенно наоборот, я написал "это", а сейчас вам предстоит всё "это" прочитать, а самое главное понять. Сегодня мы с вами посвятим время такой теме, как "Системы голосований на РНР".
Да, на сегоднешний день этой довольно интересной теме посвященны целые горы статей, но как-то маловато статей рассказывают всё чётко и во всех мелочах. Именно про эти мелочи и чётко мы сейчас и поговорим. Наверное больше половины из вас участвовали в разных интерактивных голосования, форумных pool'ах, и в прочей дребедени. Но как это всё построено ? На каком алгоритме это всё "пашет" ?
А алгоритмов существует великое множество. Сейчас я перечислю самые популярные среди разработчиков:
XML- вопрос и ответы, храняться в одном XML файле, с которого через парсер и "достают" все данные для обеспечения работы голосований.
Хранение данных в файле - этот тип хранения более популярен чем его XML-ий товарищ. Принцип такой: вопрос помещаеться в первую строку файла, после построчно выводятся варианты ответа, а через некий разделитель количество голосов. Однако в отличие от предыдущего данный метод требует больше времени для написания, и немного ограничивает возможности.
Хранение данных в БД (способ №1) - этот способ являеться самым рациональным, ведь он не требует особых ментальных усилий, при этом обеспечивая высокую продуктивнось, но для него, во-первых, требуеться наличие БД, во-вторых, этот метод занимает много физической памяти базы данных. Мы рассмотрим его в самом начале. Принцип таков: вопрос и прочие статические данных располагаються в некой абстрактной таблице "а", при этом ответы в таблице "б". На каждый ответ приходится ряд "р", таблице "а", который имеет ссылку на номер голосования находящегося в таблице "а". То есть между таблицей "а" и "б" устанавливаеться прямая связь. Почему я сказал, что этот способ требует немалого пространства в БД ? Так потому, что под каждый ответ выделяеться отдельный столбец, а это крайне не рационально в случае при работе с БД.
Хранение данных в БД (способ №2) - этот способ самый сложный среди всех вышеперечисленных, однако он не занимает много место, при этом он совмещает ответы, вопрос, и количество ответов в отной таблице, и в этой таблице ряд отводиться под всё голосование в целом, хотя эта компактность компенсируеться повышеной сложностью обновления данных, и засчёт каждого нового голоса. Принцип в том, что все ответы помещаються в одну строку, при чём в особом порядке, так, что позиции каждого ответа соответствует позиция значения количества голосов, людей которые проголосовали именно за этот вариант. Так же поле вариантов ответов, и соответственно количества голосов будут кодироваться в формате base64, для уменьшения размера конечной строки.
Всё, это были все варианты выполнения алгоритма процесса голосования, которые мы сегодня рассмотрим.
Лично мой выбор второй вариант хранения в БД, так как я автор данного способа, и он мне более по душе, но тем кто только начинает осваивать технологии Веб-программирования, я советую почитать про первый вариант при работе с БД. Что ж, теперь можно перейти к практической части, и она начнёться именно с первого варианта при работе с БД. Но я забыл сказать про довольно важную на мой взгляд деталь. Я забыл сказать про защиту от повторного голосования, это мы будем делать во всех голосования при помощи БД, поскольку именно этот способ обеспечивает полную целостность данных, ибо для доступа к БД третьим лицам им нужно будет иметь данные для доступа или взломать БД, что крайне проблематично, поэтому хакерам будет довольно сложно нанести скрипту урон в этой части. А файлы всегда можно удалить, даже если на них будет блокировка "666", то всё равно они защищены не в полном объёме.
Поэтому я выбрал БД. Посему сейчас вам нужно создать табличку для храния данных о пользователях которые уже проголосовали.
Структура таблички такова:
ТАБЛИЦА `alredy_vote`: id- BIGINT- AUTO_INCREMENT- PRIMARY KEY vote_id- BIGINT- NOT NULL- UNIQUED ip- TEXT- NOT NULL
Название оставляю вам, но скажу что я буду использовать "alredy_voted". Что ж, а теперь на практику :)
Первый вариант при работе с БД.
Что ж, по моему мнению для тех кто называет себя программистами на "достаточном" уровне хватило бы одного только описания в начале, но во всяком случае я опишу данный способ со всеми частицами. Итак, с начала мы создадим две таблички в вашей базе данных, называйте их как хотите, но в статье я буду использовать названия "pools_answs" (для вариантов ответа) и "pools" (для самих голосований). Их структура сдедующая:
ТАБЛИЦА `pools`: id- BIGINT- AUTO_INCREMENT- PRIMARY KEY question- TEXT- NOT NULL status- ENUM('on','off')- DEFAULT 'on'- NOT NULL
ТАБЛИЦА `pools_answs`: id- BIGINT- AUTO_INCREMENT- PRIMARY KEY vote_id- BIGINT- NOT NULL- UNIQUED value- TEXT- NOT NULL
Вот, это структура таблиц для работы нашей системы, я привожу лишь их сруктуру, поскольку если вы даже языка SQL не знаете, то вам делать здесь явно нечего. Итак, первой нашей с вами функцией будет функция для добавления голосований.
Так же хочу упомянуть, что пользователь сам может устанавливать количество вариантов ответа. Конечно есть соблазн использовать DOM модель и любимый нами всеми JS (ведь так ?), но я воздержусь, и воспользуюсь простым и банальным параметром QUERY_STRING и старым, родимым $_GET интерфейсом. Что ж, давайте попытаемся воплотить всё это безобразие на "холсте" :)
<? $count=isset($_GET['count'])? $_GET['count']:5; if(!isset($_POST['add'])){ print"<form action='' method='post' name='addPool'>"; print"<table width='300' height='50' align='center'>"; print"<tr><td colspan='2' style='text-align:center;'><input size='40' type='text' name='question' value='Введите вопрос голосования' onFocus='this.select();'></td> </tr>"; print"<tr><td style='text-align:center;' colspan='2'><button onClick="top.location='".$_SERVER['PHP_SELF']."?count=".($count+1)."'"> Добавить вариант</button></td></tr>"; for($i=0;$i<$count;$i++){ print"<tr><td>Вариант ответа №".$i.":</td><td><input type='text' name='answs[]'></td></tr>"; } print"<tr><Td colspan='2' style='text-align:center;'><input type='submit' name='add' value='Добавить'></td></tr>"; print"</table>"; print"</form>"; }else{ $question=$_POST['question']; $answs=$_POST['answs']; if(trim($question)==''){ die("Вы не ввели вопрос !"); } $count=0; for($i=(count($answs)-1);$i>=0;$i--){ if(trim($answs[$i])==''){ $count++; } if($count==count($answs) || (count($answs)-$count)<2){ die('Должно быть как минимум 2 варианта ответа!'); } } $conn_id=@mysql_connect("localhost","root","") or die("Ошибка соединения с сервером БД !"); @mysql_select_db("shockstudio"); $check=@mysql_query("SELECT id FROM `pools` WHERE question='".$question."'", $conn_id) or die("Ошибка запроса к БД !"); if(@mysql_num_rows($q)!=0){ die("Голосование с таким вопросом уже существует !"); } unset($check); $q=@mysql_query("INSERT into `pools` VALUES('','".$question."','on')",$conn_id) or die("Ошибка запроса к БД !"); unset($q); $q=@mysql_query("SELECT id FROM `pools` WHERE question='".$question."'",$conn_id) or die("Ошибка во время запроса к серверу !"); $row=@mysql_fetch_array($q); $id=$row['id']; unset($q,$row); for($i=(count($answs)-1);$i>=0;$i--){ if(trim($answs[$i])!=''){ $q=@mysql_query("INSERT into `pools_answs` VALUES('','".$id."','".$answs[$i]."', '')",$conn_id) or die("Ошибка запроса к БД !"); } } } ?>
Что ж, это и есть функциональная база для добавления голосования в базу данных. Давайте остановимся на этом и рассмотрим всё детальней.
С самого начала мы получаем количество вариантов ответа, которые нужно вывести для редактирования. Мы объявляем переменную `count`, которая в соответствии от существования переменной `count` в QUERY_STRING будет принимать значение или переменной QUERY_STRING (если такая сущестует) и значение по умолчанию, а именно 5. После этого мы работаем над пользовательским интерфейсом, и при этом создаём интересную кнопочку, которая и служит для добавления варианта ответа. При её нажатии значение переменной `count` помещаеться в строку запроса в броузере, и при этом она увеличиваеться на единицу. Вот как всё просто, прям до ужаса. Далее всё просто, и вот мы перешли к обработке полученных из формы данных.
В начале обработки идёт процесс валидации данных. Мы проверям заполнены ли поля формы, а после этого количество вариантов ответа. Для проверки заполненности полей "ответов", мы объявляем переменную $count, которая будет каждый раз увеличиваться, если $i-й элемент массива будет не заполнен.
После, если количество незаполненных полей ($count), будет равно общей сумме вариантов ответа, или их разница будет менее 2-х, то будет возбужденно исключение, которое сообщит пользователю о том, что нужно ввести минимум 2 варианта ответов. На этом проверка данных завершаеться, и мы переходим к валидации данных в БД. Сначала нам нужно проверить, не существует ли уже голосования с таким вопросом, и если существует то возбуждаем ошибку. Если всё же нет, то добавляем данные в таблицу.
Следующим шагом будет получение индификатора (id) текущей записи, для добавления вариантов ответа в таблицу. После того как мы получили ID, мы снова делаем перебор, только теперь уже с иной целью. Сейчас мы будем добавлять каждый i-й элемент массива, при условии что он не пустой, в таблицу для ответов, при этом мы так же добавляем и индификатор голосования к которому будет привязан данных ответ. Ну и в случае успеха, выводим сообщение о том, что всё прошло успешно. Как видите, без учёта некоторых моментов, всё довольно легко. Но это только первый кусочек той большой мозаики общей функциональностию. Сейчас мы переходим к следующей части, которая и будет неким тестом предыдущей, а именно воплотим сам механизм голосования через интерфейс сайта. В контексте данного варианта исполнения, это займёт почти несколько строк, так как нам всего-то навсего нужно обновить поле в одной таблице, и занести данные в другую.
Что ж, вот мой вариант исполнения данной задачи:
<? if(!isset($_POST['vote'])){ print"<form action='' method='post' name='vote'>"; print"<table width='400' height='50' align='center'>"; $conn_id=@mysql_connect("localhost","root","") or die("Ошибка соединения с сервером БД !"); @mysql_select_db("db"); $q=@mysql_query("SELECT * FROM `pools` WHERE status='on'",$conn_id) or die("Ошибка запроса к БД !"); if(@mysql_num_rows($q)==0){ echo"Голосования не найдены !"; }else{ $id=mt_rand(1,@mysql_num_rows($q)); unset($q); $q=@mysql_query("SELECT * FROM `pools` WHERE id='".$id."'",$conn_id) or die("Ошибка запроса к БД !"); $row=@mysql_fetch_array($q); print"<tr><Td colspan='2'>Q: ".$row['question']."</td> </tr>"; unset($q); $vote_check=@mysql_query("SELECT id FROM `alredy_vote` WHERE ip='". $_SERVER['REMOTE_ADDR']."'",$conn_id) or die("Ошибка запроса к БД !"); $q=@mysql_query("SELECT id,value FROM `pools_answs` WHERE vote_id='". $id."'",$conn_id) or die("Ошибка запроса к БД !"); if(@mysql_num_rows($q)==0){ die("Вопросы не найдены !"); }else{ while($row=@mysql_fetch_array($q)){ $row2=@mysql_fetch_array($q2); if(@mysql_num_rows($vote_check)!=0){ $q2=@mysql_query("SELECT count FROM `pools_answs` WHERE id='".$row['id']."'", $conn_id) or die("Ошибка запроса к БД !"); print"<tr><td>".$row['value']."</td><td>". $row2['count']."</td></tr>"; }else{ print"<tr><td>".$row['value']."</td><td><input type='radio' name='answer' value='".$row['id']."'></td></tr>"; print"<input type='hidden' name='id' value='".$id."'>"; print"<tr><td colspan='2'><input type='submit' name='vote' value='Проголосовать'></td></tr>"; } } } } print"</table>"; print"</form>"; @mysql_close($conn_id); }else{ $id=$_POST['id']; $answer=$_POST['answer']; $conn_id=@mysql_connect("localhost","root","") or die("Ошибка во время запроса к серверу !"); @mysql_select_db("db"); $q=@mysql_query("SELECT id FROM `aredy_vote` WHERE ip='". $_SERVER['REMOTE_ADDR']."'",$conn_id) or die("Ошибка во время запроса к серверу !"); if(@mysql_num_rows($q)!=0){ print"Вы уже участвовали в данном голосовании !"; }else{ $q=@mysql_query("INSERT into `alredy_vote` VALUES('','".$id."','". $_SERVER['REMOTE_ADDR']."')",$conn_id) or die("Ошибка запроса к БД !"); unset($q); $q=@mysql_query("UPDATE `pools_answs` SER count=count+1 WHERE id='".$id. "' AND vote_id='".$_POST['answer']."'",$conn_id) or die("Ошибка запроса к БД !"); print"Ваш голос учтён. Спасибо за участие !!"; } @mysql_close($conn_id); } ?>
Источник: http://www.codenet.ru/ |