Obs艂uga bazy danych
Skrypt korzysta z relacyjnej bazy danych do przechowywania ankiet i ich wynik贸w. Mo偶e wykorzystywa膰 r贸偶ne bazy danych, zale偶nie od sterownika; bardzo prosta i elementarna klasa dla bazy MySQL, o jak膮 ja si臋 opiera艂em:
class Database {
public function __construct() {
mysql_connect('localhost', 'root', '');
mysql_select_db('ankieta');
}
聽
public function query($sql) {
return mysql_query($sql);
}
聽
public function numrows($sql) {
return mysql_num_rows($sql);
}
聽
public function fetch($sql) {
return mysql_fetch_array($sql);
}
}
U偶yte zapytania SQL powinny dzia艂a膰 tak偶e pod PostgreSQL i SQLite (nie wiem jak inne bazy), w przypadku powy偶szej klasy wystarczy zmieni膰 nazwy funkcji, np. mysql_query na pg_query. Po wi臋cej odsy艂am do聽manuala PHP. Oczywi艣cie, mo偶na te偶 skorzysta膰 z PDO, ADOdb, PEAR::DB, czy - w przypadku MySQL - mysqli.
Kod skryptu
I w ko艅cu najd艂u偶ej wyczekiwana cz臋艣膰 artyku艂u, mianowicie kod skryptu sondy. Jest to klasa, dla kt贸rej聽interfejs聽(nie jest on konieczny do dzia艂ania skryptu) mo偶na zaprojektowa膰 nast臋puj膮co:
interface iPoll {
public function display(); // zwraca caly przeparsowany kod HTML sondy
public function other($id); // inne sondy
}
Jak ju偶 wspomina艂em, do dzia艂ania wymagany jest PHP5, aczkolwiek przepisanie na oficjalnie nierozwijanego ju偶 i przestarza艂ego PHP4 nie jest wi臋kszym problemem. Aby tego dokona膰, nale偶y:
pozby膰 si臋 wielopoziomowych odwo艂a艅 do sk艂adowych (np. stworzy膰 zmienn膮聽$base = $this->db;聽i odwo艂ywa膰 si臋聽$base->query())
pozby膰 si臋 modifikator贸w dost臋pu, tj. z funkcji usun膮膰 s艂owa kluczowe聽public, a we w艂a艣ciwo艣ciach zast膮pi膰聽public聽na聽var
zmieni膰 nazw臋 konstruktora z聽__construct聽na tak膮, jak膮 nosi nazwa klasy (Poll)
Sam wiem, i偶 najlepiej si臋 analizuje, czyta i sprawdza dzia艂anie kodu pokazanego w ca艂o艣ci, ani偶eli wiele kr贸tkich listing贸w z omawian膮 szczeg贸艂owo zasad膮 dzia艂ania. Kod jest prosty i posiada najwa偶niejsze komentarze, tak wi臋c raczej nie powinno by膰 niejasno艣ci.
class Poll {
public $db;
public $other = true; // czy pokazywac inne sondy
public $desc_sort = true; // sortowanie innych sond od najnowszych
public $id; // id sondy
public $new_fields = array(); // funkcje z nowy polami do formularza
public $no_add = false; // nie dodawac (np. ktoras z funkcji z $new_fields mowi, ze dane niepoprawne)
聽
public function __construct()
{
$this->db = new Database();
}
聽
public function display()
{
$sql = mysql_query('SELECT
q.id, q.title, q.date_begin, q.date_end, q.stop,
a.id_answer, a.answer, a.votes,
(SELECT sum(votes) FROM poll_answers WHERE id_poll = q.id GROUP BY id_poll) as sum
FROM
poll_questions as q, poll_answers as a
WHERE
q.id = a.id_poll AND q.id = ' .
(!isset($_GET['id']) ? '(SELECT max(id) FROM poll_questions)' : (int)$_GET['id']));
聽
if($this->db->numrows($sql) > 0)
{
$now = date('Y-m-d');
while($row = $this->db->fetch($sql))
{
if($_POST['vote'] && !$this->no_add)
{
$row['sum']++;
if($row['id_answer'] == $_POST['vote']) $row['votes']++;
}
聽
if(!$b)
{
$this->id = $row['id'];
if($row['stop'] == 1 || $_POST['vote'] && !$this->no_add) $noform = true;
聽
// podstawowe dane o ankiecie
$ret .= '<b>' . $row['title'] . '</b><p />艁膮cznie oddano g艂os贸w: ' . $row['sum'].
'<br />Data rozpocz臋cia: ' . $row['date_begin'] .
'<br />Data zako艅czenia: ' . $row['date_end'];
聽
if($row['date_end'] <= $now) $ret .= '<p />Ankieta si臋 ju偶 zako艅czy艂a.';
elseif($row['stop'] == 1) $ret .= '<p />Glosowanie w ankiecie zosta艂o wstrzymane.';
聽
$ret .= '<p />';
聽
// wyswietlenie formularza
if(!isset($_COOKIE['poll' . $this->id]) && $row['date_end'] > $now && !$noform)
{
$ret .= '<form action="" method="post">';
foreach($this->new_fields as $v) $ret .= $v;
$form = true;
} elseif(isset($_COOKIE['poll' . $this->id]) && $row['date_end'] > $now && !$noform)
{
$ret .= 'G艂osowa艂e艣 ju偶 w tej sondzie.<p />';
}
聽
// user zaglosowal
if($_POST['vote'] && !$this->no_add)
{
$ret .= 'Tw贸j g艂os zosta艂 dodany.<p />';
if(!isset($_COOKIE['poll' . $this->id]))
{
$this->db->query('UPDATE poll_answers SET votes=votes+1 WHERE id_answer='.$_POST['vote']);
setcookie('poll' . $this->id, $this->id, time()+360 * 360 * 30); // 22 lata
}
$noform = true;
}
$b = true;
}
聽
// wyswietlenie wariantow odpowiedzi
if($form)
$ret .= '<input type="radio" name="vote" value="' . $row['id_answer'] . '" /> ' .
$row['answer'] . '<br />';
else
{
$ret .= $row['answer'].', ' . $row['votes'] . ' glosow, ' .
($row['sum'] > 0 ? round($row['votes']*100/$row['sum']) : 0) . '% ' .
'<div style="background: red; height: 10px; width: ' .
($row['votes'] == 0 || $row['sum'] == 0 ? 5 : round($row['votes'] * 200 / $row['sum'])) .
'px"></div><br />';
}
}
if($form) $ret .= '<br /><input type="submit" name="submit" value="G艂osuj!" /></form>';
if($this->other) $ret .= '<p /><b>Inne sondy</b><p />' . $this->other($this->id);
}
else $ret = 'Nie ma takiej sondy w bazie.';
return $ret;
}
聽
public function other($id)
{
$sql = 'SELECT id, title FROM poll_questions WHERE id <> ' . $id . ' ORDER BY id ' . ($this->desc_sort ? 'DESC' : 'ASC');
$sql = $this->db->query($sql);
if($this->db->numrows($sql) > 0)
{
$ret = '<ul>';
while($row = $this->db->fetch($sql))
$ret .= '<li><a href="' .$_SERVER['PHP_SELF'] . '?id=' . $row['id'] . '">' . $row['title'] . '</a></li>';
return $ret . '</ul>';
}
else return '(brak)';
}
}
Jak teraz tego u偶y膰? Najpro艣ciej w ten spos贸b:
$poll = new Poll;
echo $poll->display();
Je艣li nie chcemy wy艣wietli膰 listy z innymi sondami znajduj膮cymi si臋 w bazie, wystarczy przed wy艣wietleniem
(metoda聽display) da膰:
$poll->other = false;
Domy艣lnie inne ankiety sortowane s膮 od najnowszej do najstarszej. Aby odwr贸ci膰 kolejno艣膰:聽
$poll->desc_sort = false;