БлогNot. Ещё одна голосовалка на JQuery+PHP :)

Помощь дата->рейтинг Поиск Почта RSS канал Статистика nickolay.info Домой

Ещё одна голосовалка на JQuery+PHP :)

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

Смысл использования JQuery - возможность встроить голосование не в отдельный HTML-фрейм или устанавливаемый на сервер скрипт, а непосредственно в HTML-содержимое. Тогда, зная id опроса и адрес скрипта, к которому он обращается, можно разместить форму на произвольном числе страниц (одного домена, конечно). Ну и при любом заходе юзверьков на эти страницы они будут видеть актуальное состояние опроса :)

Вот "голосование по умолчанию" непосредственно в работе (в новой вкладке, а не встроено в ленту, т.к. скрипты лежат в другом домене и вызываю без фрейма).

Обратите внимание, что когда вы проголосуете, страничка, на которую выведен опрос, всё равно перезагрузится - мы же отправляли данные на сервер методом POST.

Дело в том, что Javascript (клиентский) в принципе не может обратиться к MySQL (серверному). Серверный Javascript, типа NodeJS, может, но он должен быть установлен на сервере, а не формироваться на страничке в вашем браузере.

Мы можем только обратиться к серверному скрипту (например, написанному на PHP) и передать ему данные, а он уже сделает с ними то, что нужно.

Код файла с примером вызова скрипта (index.html):

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
</head>
<body> 
<p>Здесь любое содержимое.

<div id="responsecontainer"></div>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
 $.ajax({
  type: "POST",
  url: "display.php",
  data: { id:1 },
  dataType: "html",
  success: function(response) {
   $("#responsecontainer").html(response);
  },
  error: function() {
   $("#responsecontainer").html("<p>Ошибка получения данных с сервера через AJAX!</p>");
  }
 });
});
</script>
<noscript><p>Извините, для работы скрипта нужен включённый в браузере Javascript!</p></noscript>

<p>Здесь тоже любое содержимое.</p>
</body></html>

Файл jquery.min.js берётся с с сервера Гугля, а в остальном ajax-код соверешенно типовой.

Для вывода блока с опросом используется контейнер с именем responsecontainer.

Вместо id:1 можно передать id другого опроса, если я таковой создам в базе.

Поговорим о структуре данных.

Мне было нужно, чтобы "накрутки" повторным голосованием были минимальными, то есть, скрипт определяет и сохраняет в базе данных IP-адреса проголосовавших и, на всякий случай, метку времени голосования. Это уже одна таблица в MySQL, пусть назвается jqueryvotingresults.

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

В соответствии со сказанным, вот SQL-запрос, создающий таблицы данных с одной предустановленной записью об "опросе по умолчанию" с id=1:

DROP TABLE IF EXISTS jqueryvoting;
CREATE TABLE IF NOT EXISTS jqueryvoting (
  id int,
  name varchar(80),
  data text,
  results text
);
DROP TABLE IF EXISTS jqueryvotingresults;
CREATE TABLE IF NOT EXISTS jqueryvotingresults (
 id int,
 ip varchar(15),
 var int,
 date bigint
);
insert into jqueryvoting (id, name, data, results) values (
 "1", "Как вам такой опрос?", 
 "Угу, просто\nНеа, слишком просто\nНи фига не понял", 
 "0\n0\n0"
);

Как всегда, это можно выполнить в phpMyAdmin.

Кроме того, хотелось посмотреть, можно ли сделать так, чтобы ajax нормально возвращал данные из базы, хранящиеся в кодировке windows-1251. Это оказалось вполне возможно, если правильно указать тег кодировки в документе (см. index.html), хранить данные в базе в той же кодировке, указать её же для MySQL (см. функцию get_conid в functions.php) и, наконец, не забыть отправить соответствующий заголовок перед тем, как возвращать содержимое из PHP-скрипта, вызванного со стороны клиента через Ajax (см. вызов header в верхней части display.php).

Всю работу выполняет как раз display.php - смотрит, есть ли в базе опрос с запрошенным id, а также есть ли уже для IP-адреса пользователя запись о голосовании. Если да - показывает результаты опроса на момент загрузки страницы, если нет - предлагает проголосовать.

<?php
 require_once 'functions.php';
 header('Content-type: text/html; charset=windows-1251');
 $ip = get_ip();
 $value = '';
 if (!isset($_POST['id'])) {
  $value = 'Не передан ID голосования';
 }
 else {
  $id = intval($_POST['id']);
  if ($id<1) {
   $value = 'Неверный ID голосования';
  }
  else {
   $result1 = dbquery('select * from '.DB_DATA.' where id="'.$id.'" limit 0,1');
   if (!$result1) {
    $value = 'Неверный запрос 1: '.mysql_error();
   }
   else {
    if (!dbrows($result1)) {
     $value = 'Не найдено данных для голосования с ID='.$id;
    }
    else {
     $note = dbfetcha($result1);
     $data = explode("\n",$note['data']);
     $results = explode("\n",$note['results']);
     if (count($data)<2 or count($results)<2 or count($data)!=count($results)) {
      $value = 'Неверные данные в таблице '.DB_DATA.' для записи '.$note['id'].':<br>'.
       $note['data'].'<br>и'.$note['results'];
     }
     else {
      $result2 = dbquery('select * from '.DB_RESULTS.' where ip="'.$ip.'" and id="'.$id.'" limit 0,1');
      if (!$result2) {
       $value = 'Неверный запрос 2: '.mysql_error();
      }
      else {
       if (!dbrows($result2)) {
        $value = '<form method="post" action="'.get_server_url().
         'vote.php" name="jqueryvotingform"><p><b>'.$note['name'].'</b></p>'."\n";
        $value .= '<input type="hidden" name="id" value="'.$id.'">'."\n";
        for ($i=0; $i<count($data); $i++) {
         $value .= '<p><input name="q" value="'.($i+1).'" type="radio"> '.
          '<span onclick="javascript:document.jqueryvotingform.q['.$i.'].checked=true;">'.
          $data[$i].
          '</span></p>';
        }
        $value .= '<input type="submit" value="Ответить"></form>';
       }
       else {
        $value = '<p><b>'.$note['name'].'</b></p>';
        $all = array_sum($results);
        for ($i=0; $i<count($data); $i++) {
         $ps = round($results[$i]/$all * 100.,1);
         $value .= '<p>'.$data[$i].': <b>'.$results[$i].'</b> ('.$ps.'%)</p>';
        }
        $user = dbfetcha($result2);
        $value .= '<p><b>Ваш голос</b>: "'.$data[$user['var']-1].'" ('.date("d.m.Y, H:i",$user['date']).')</p>';
       }
      }
     }
    }
   }
  }
 }
 echo $value;
?>

Обычные радиокнопки HTML неудобны тем, что можно щелкать только по кружочку, а не по тексту, поясняющему выбор. С помощью Javascript в форме опроса можно щёлкать и непосредственно по текстам вариантов ответа.

Если пользователь нажал кнопку "Ответить", в дело вступает файл vote.php. Он проверит правильность полученных данных, если всё верно, добавит в таблицу результатов запись о голосовании и перенаправит выполнение на ту страницу, откуда его вызвали.

Обращаю внимание, что "сделать всё внутри Ajax", без перезагрузки страницы вообще, по-моему, при таком подходе нельзя.

Представьте, что мы засунули в vote.php ещё один программно формируемый ajax-запрос, который должен отправить на сервер выбор пользователя, сделанный им при голосовании. Но выбор-то в момент загрузки страницы ещё не сделан, не выбирать же за клиента одну из радиокнопок? Нельзя вытащить самого себя за волосы из болота.

Можно было бы полностью сформировать опрос на стороне клиента с помощью Javascript и передать данные о выборе пользователя Ajax-запросом на сервер, вызванный PHP-скрипт внесёт текущие данные, снова прочитает из базы изменения и вернёт новые данные как HTML-контент. Тогда страница не будет перегружаться при голосовании, но для каждого нового опроса нужно писать новый код на Javascript, а это - совсем другая задача.

<?php
 require_once 'functions.php';
 $ip = get_ip();
 $value = '';
 if (isset($_POST['q']) and !empty($_POST['q'])) {
  $q = intval($_POST['q']);
  if ($q>0) {
   if (isset($_POST['id']) and !empty($_POST['id'])) {
    $id = intval($_POST['id']);
    if ($id>0) {
     $result1 = dbquery('select * from '.DB_DATA.' where id="'.$id.'" limit 0,1');
     if ($result1 and dbrows($result1)) {
      $note = dbfetcha($result1);
      $data = explode("\n",$note['data']);
      $results = explode("\n",$note['results']);
      if (count($data)>1 and count($results)>1 and count($data)==count($results)) {
       if ($q<=count($data)) {
        $result2 = dbquery('select * from '.DB_RESULTS.' where ip="'.$ip.'" and id="'.$id.'" limit 0,1');
        if ($result2) {
         if (!dbrows($result2)) {
          $result3 = dbquery('insert into '.DB_RESULTS.' (id, ip, var, date) '.
           'values ("'.$id.'", "'.$ip.'", "'.$q.'", "'.time().'")');
          if ($result3) {
           $results[$q-1]++;
           $new_results = implode ("\n",$results);
           $result4 = dbquery('update '.DB_DATA.' set results = "'.$new_results.'" where id = "'.$id.'"');
          }
         }
        }
       }
      }
     }
    }
   }
  }
 } 
 if (isset ($_SERVER['HTTP_REFERER'])) {
  header('Location: '.$_SERVER['HTTP_REFERER']);
 }
 else {
  header('Location: '.get_server_url());
 }
?>

Служебный файл functions.php, как обычно, содержит нужные для работы функции:

<?php

 require_once('config.php');

 if (!isset($conid)) {

  function dbconnect() {
   $mysql=mysql_connect(DB_HOST, DB_USER, DB_PASS); //хост, логин, пароль
   mysql_select_db(DB_NAME); //имя базы данных
   return $mysql;
  }

  function dbquery ($sql) { return mysql_query($sql,get_conid()); }
  function dbrows ($result) { return mysql_num_rows($result); }
  function dbfree ($result) { mysql_free_result($result); }
  function dbclose ($conid) { mysql_close(get_conid()); }

  function dbfetcha ($result) {
   if ($row=mysql_fetch_assoc ($result)) return $row;
   else return false;
  }

  function dbfetch ($result){
   if ($row=mysql_fetch_array($result)) return $row;
   else return false;
  }
 
  function get_conid () {
   static $conid=0;
   if ($conid==0) {
    $conid=dbconnect();
    mysql_set_charset (DB_ENCODING,$conid);
    dbquery("SET NAMES ".DB_ENCODING,$conid);
   }
   return $conid;
  }

  get_conid ();
 }

 function get_ip () {
  if( getenv('HTTP_X_FORWARDED_FOR') != '' ) {
   $client_ip = ( !empty($_SERVER['REMOTE_ADDR']) ) ? $_SERVER['REMOTE_ADDR'] : 
    ( ( !empty($_ENV['REMOTE_ADDR']) ) ? $_ENV['REMOTE_ADDR'] : $REMOTE_ADDR );
   $entries = explode(',', getenv('HTTP_X_FORWARDED_FOR'));
   reset($entries);
   while (list(, $entry) = each($entries)) {
    $entry = trim($entry);
    if ( preg_match("/^([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)/", $entry, $ip_list) ) {
     $private_ip = array('/^0\./', '/^127\.0\.0\.1/', '/^192\.168\..*/', 
      '/^172\.((1[6-9])|(2[0-9])|(3[0-1]))\..*/', '/^10\..*/', '/^224\..*/', '/^240\..*/');
     $found_ip = preg_replace($private_ip, $client_ip, $ip_list[1]);
     if ($client_ip != $found_ip) {
      $client_ip = $found_ip;
      break;
     }
    }
   }
  }
  else {
   $client_ip = ( !empty($_SERVER['REMOTE_ADDR']) ) ? $_SERVER['REMOTE_ADDR'] : 
    ( ( !empty($_ENV['REMOTE_ADDR']) ) ? $_ENV['REMOTE_ADDR'] : $REMOTE_ADDR );
  }
  return $client_ip;
 }

 function get_server_url () {
  $s='http://'.$_SERVER['SERVER_NAME'].$_SERVER['SCRIPT_NAME'];
  $sb=strrchr($s,"/");
  if ($sb!==false) $s=substr($s,0,-(strlen($sb)-1));
  return $s;
 }
?>

Файл настроек config.php хранит в одном месте все нужны настройки скрипта. На реальном хостинге значения его констант, разумеется, изменятся.

<?php
 define('DB_HOST', 'localhost'); //Хост, на котором размещен сервер MySQL
 define('DB_USER', 'root'); //Имя пользователя MySQL
 define('DB_PASS', 'root'); //Пароль пользователя MySQL
 define('DB_NAME', 'blognot'); //Имя базы данных
 define('DB_DATA', 'jqueryvoting'); //Имя таблицы с данными опросов
 define('DB_RESULTS', 'jqueryvotingresults'); //Имя таблицы с результатами опросов
 define('DB_ENCODING', 'cp1251'); //Кодировка базы данных. utf8 для Юникода
?>

Вот примерно всё, написать на PHP админку для такого скрипта - абсолютно тривиальная задача, я этого делать не стал. Проект не архивирую, так как в будущем коды могут измениться, все они приведены в этой заметке. А в общем, бред какой-то получился, убил хороший вечер :)


теги: php учебное jquery

комментарии (0)

10.02.2016, 21:12; рейтинг: 4025

  в началопоиск по блогунаписать авторустатистика

Наверх Яндекс.Метрика
© PerS
вход