Ten artykuł jest archiwalny, korzystasz z niego na własną odpowiedzialność.
System ankiet w PHP
System ankiet to bardzo przydatne narzędzie. Dzięki niemu możemy sprawdzić np. co użytkownicy myślą o stronie lub jakiej przeglądarki używają.
Przedstawione przeze mnie rozwiązanie będzie się opierało na obiektowym PHP5 oraz bazie danych MySQL. Na wstępie zaznaczę że niektóry kod jest bardzo podobny do tego w tutorialu o Systemie podstron.
Na początek należy stworzyć odpowiednie tabele w bazie danych. Będą to ankiety (będzie przechowywać nazwę ankiety i określać która jest aktywna), odpowiedzi (nazwa odpowiedzi wraz z ilościami głosów) i glosy (ta tabela będzie przechowywać ip głosujących aby nie mogli ponownie głosować, będzie też zabezpieczenie na ciasteczka).
CREATE TABLE ankiety (
id int(11) NOT NULL AUTOINCREMENT,
tytul varchar(255) NOT NULL,
aktywna int(11) NOT NULL,
PRIMARY KEY (id)
) ENGINE=MyISAM ;
CREATE TABLE odpowiedzi (
id int(11) NOT NULL AUTOINCREMENT,
id_ankiety int(11) NOT NULL,
tytul varchar(255) NOT NULL,
ilosc int(11) NOT NULL,
PRIMARY KEY (id)
) ENGINE=MyISAM ;
CREATE TABLE glosy (
id int(11) NOT NULL AUTO_INCREMENT,
id_ankiety int(11) NOT NULL,
ip varchar(255) NOT NULL,
PRIMARY KEY (id)
) ENGINE=MyISAM ;Jeśli stworzyliśmy już tabelę w bazie danych czas zrobić plik ankiety.class.php. To plik w którym będą trzymane wszystkie funkcje klasy Strony.
<?php
class Ankiety {
// Hasło do panelu admina
private $haslo = 'haslo123';
// Host bazy danych
private $dbhost = 'localhost';
// Użytkownik bazy danych
private $dbuser = 'root';
// Hasło do bazy danych
private $dbpass = '';
// Nazwa bazy danych
private $dbname = 'test';
function __construct() {
mysql_connect( $this->dbhost, $this->dbuser, $this->dbpass ) or die(mysql_error());
mysql_select_db( $this->dbname ) or die(mysql_error());
mysql_query("SET NAMES utf8");
mysql_query("SET CHARACTER SET utf8");
mysql_query("SET collation_connection = utf8_polish_ci");
}
function __destruct() {
mysql_close();
}
// inne funkcje
}
?>W powyższym kodzie stworzyliśmy klasę Ankiety wraz z prywatnymi zmiennymi (co one przechowują jest opisane w komentarzach). Dodatkowo w konstruktorze klasy jest połączenie z bazą danych. Przy połączeniu ustawiamy kodowanie na UTF-8 gdyż jest ono bardzo zalecane i będziemy z niego korzystać w tym tutorialu. W destruktorze rozłączamy się z bazą danych. Tam gdzie jest zaznaczone komentarzem będziemy wklejać inne funkcje.
Na początek stworzymy sobie funkcję pokazującą aktywną ankiete.
function pokaz() {
// 1
if(isset($_POST['odpowiedz'])) $this->dodaj_odpowiedz($_POST['id_ankiety'], $_POST['odpowiedz']);
$result = mysql_query("SELECT * FROM ankiety WHERE aktywna = '1' LIMIT 1");
$row = $this->stripslashes_deep( mysql_fetch_array($result) ); // 2
$glosowal = $this->glosowal($row['id']); // 3
// 4
$odpowiedzi = mysql_query("SELECT * FROM odpowiedzi WHERE id_ankiety = '{$row[id]}' ORDER BY ilosc DESC");
$odp = Array();
$glosow = 0;
while($row_odp = mysql_fetch_array($odpowiedzi)) { // 5
$odp[$row_odp['id']] = $this->stripslashes_deep( $row_odp );
$glosow = $glosow + $row_odp['ilosc'];
}
echo '<div id="ankieta">
<h3>'.$row['tytul'].' (głosów: '.$glosow.')</h3>'; // 6
if($glosowal) { // 7
foreach($odp as $odpowiedz) {
// 8
$procenty = ($odpowiedz['ilosc']!='0') ? (round(($odpowiedz['ilosc']/$glosow)*100)) : '0';
echo '<div class="odpowiedz">
<b>'.$odpowiedz['tytul'].'</b> <span class="ilosc">'.$procenty.'% ('.$odpowiedz['ilosc'].')</span>
<div class="pasek" style="width: '.$procenty.'%"></div>
</div>';
}
} else { // 9
ksort($odp);
echo '<form action="" method="POST">';
foreach($odp as $odpowiedz) {
echo '<div class="odpowiedz">
<input type="radio" name="odpowiedz" value="'.$odpowiedz['id'].'"> '.$odpowiedz['tytul'].'<br>
</div>';
}
echo '<input type="hidden" name="id_ankiety" value="'.$row['id'].'">
<input type="submit" value="Głosuj">
</form>';
}
echo '</div>';
}- Jeśli jest w użyciu $POST['odpowiedz'] to wykonujemy funkcję dodajodpowiedz() wraz z id ankiety i id odpowiedzi.
- Wykonujemy zapytanie na aktywną ankietę i używamy na wynikach funkcji stripslashes_deep którą stworzymy później.
- Sprawdzamy czy użytkownik już oddał głos, jako parametr do funkcji podajemy id ankiety.
- Wykonujemy zapytanie na wszystkie odpowiedzi sortując wg. ilości oddanych głosów.
- W pętli while pobieramy rekordy i dodajemy je do tablicy $odp. Używamy tutaj też funkcji stripslashes_deep. Przy okazji obliczamy ilość oddanych głosów (można by to zrobić odpowiednim zapytaniem do bazy danych ale po co nam dodatkowe zapytanie ;) ).
- Wyświetlamy tytuł ankiety wraz z ilością głosów.
- Jeśli użytkownik głosował to wyświetlamy odpowiedzi wraz z ilością oddanych na nie głosów.
- Obliczamy ilość procent oddanych głosów na daną odpowiedź. ilość głosów na odpowiedź/ilość wszystkich głosów*100
- Jeśli użytkownik nie głosował to sortujemy tablicę z odpowiedziamy wg. klucza (czyli wg. ID) i wyświetlamy radio-buttony z odpowiedziami.
Troche to skomplikowane ale da radę się w tym połapać. Teraz stworzymy funkcję na dodawanie odpowiedzi.
function dodaj_odpowiedz($id_ankiety, $odpowiedz) {
if($this->glosowal($id_ankiety)) { // 1
echo '<p>Już głosowałeś w tej ankiecie!</p>';
} else {
$ip = $_SERVER['REMOTE_ADDR'];
mysql_query("UPDATE odpowiedzi SET ilosc = ilosc + 1 WHERE id = '$odpowiedz'");
mysql_query("INSERT INTO glosy (`id_ankiety`, `ip`) VALUES ('$id_ankiety', '$ip')"); // 2
$ob = ini_get('output_buffering');
if(!headers_sent() || strtolower($ob) == 'on' ) { // 3
setcookie("ankieta_$id_ankiety", "1", time()+(60*60*24*365));
}
}
}- Najpierw sprawdzamy czy użytkownik już głosował.
- Pobieramy IP, zwiększamy ilość głosów o 1 i dodajemy do tabeli glosy jeden głos wraz z IP.
- Tutaj mamy trochę zabezpieczeń aby nam nie wyskakiwały błędy. Jeśli nagłowki nie zostały wysłane albo jest włączone buforowanie to wysyłamy ciasteczko z ID ankiety.
Teraz czas na funkcję sprawdzającą czy użytkownik już oddał głos w ankiecie.
function glosowal($id) {
$ip = $_SERVER['REMOTE_ADDR'];
$result = mysql_query("SELECT id FROM glosy WHERE ip = '$ip' AND id_ankiety = '$id'");
$num_rows = mysql_num_rows($result);
return ($num_rows > 0 || isset($_COOKIE['ankieta_'.$id])) ? true : false;
}Pobieramy z tabeli wszystkie rekordy z danym IP i id ankiety. Jeśli znalazło jakieś rekordy lub istnieje odpowiednie ciasteczko to zwracamy true (głosował) w przeciwnym wypadku false (nie głosował).
Teraz trzeba zająć się panelem admina. Będziemy tam mogli dodawać, edytować i usuwać ankiety - wszystko to czego potrzebujemy ;)
function admin() {
if(!isset($_SESSION['admin']) || $_SESSION['admin'] != true) {
$this->admin_logowanie();
return;
}
switch($_GET['akcja']) {
case 'dodaj':
$this->admin_dodaj();
break;
case 'edytuj':
$this->admin_edytuj($_GET['id']);
break;
case 'usun':
$this->admin_usun($_GET['id']);
break;
default:
$this->admin_lista();
}
}Na początek sprawdzamy czy użytkownik jest zalogowany, jeśli nie to wyświetlamy formularz logowania, w przeciwnym wypadku zależnie od akcji wykonujemy odpowiednią funkcję, domyślnie jest to lista ankiet.
function admin_logowanie() {
if(isset($_POST['haslo'])) {
if($_POST['haslo'] == $this->haslo) $_SESSION['admin'] = true;
else echo '<p>Hasło niepoprawne!</p>';
}
if(!isset($_SESSION['admin']) || $_SESSION['admin'] != true) {
echo '<form action="" method="POST">
<label>Hasło:</label><br>
<input type="password" name="haslo"><br>
<input type="submit" value="Zaloguj">
</form>';
} else {
echo '<p>Jesteś zalogowany! <a href="admin.php">Przejdź do strony głównej</a>.';
}
}Bardzo prosta funkcja na logowanie.
function admin_lista() {
$result = mysql_query("SELECT * FROM ankiety ORDER BY id DESC");
if(mysql_num_rows($result) > 0) {
echo '<table>';
while($row = mysql_fetch_array($result)) {
echo '<tr>
<td'.(($row['aktywna']) ? ' style="font-weight:bold"' : '').'>'.$this->stripslashes_deep($row['tytul']).'</td>
<td>
<a href="admin.php?akcja=edytuj&id='.$row['id'].'">Edytuj</a> |
<a href="admin.php?akcja=usun&id='.$row['id'].'">Usuń</a>
</td>
</tr>';
}
echo '</table>';
} else {
echo '<p>Brak ankiet!</p>';
}
echo '<p><a href="admin.php?akcja=dodaj">Dodaj nową ankietę</a></p>';
}Tradycyjna funkcja na wyświetlanie rekordów z bazy wraz z linkami do edycji i usuwania. Jedyny dodatek jest taki że napis aktywnej ankiety jest pogrubiony. Na dole mamy link do dodawania ankiety.
function admin_dodaj() {
if(isset($_POST['tytul'])) { // 1
$tytul = $this->czysc($_POST['tytul']);
if($_POST['aktywna']=='1') { // 2
$aktywna = '1';
mysql_query("UPDATE ankiety SET aktywna = '0'");
} else $aktywna = '0';
// 3
$result = mysql_query("INSERT INTO ankiety (`tytul`, `aktywna`) VALUES ('$tytul', '$aktywna')");
$id = mysql_insert_id();
// 4
foreach($_POST['odpowiedzi'] as $odpowiedz) {
if(!empty($odpowiedz)) mysql_query("INSERT INTO odpowiedzi (`id_ankiety`, `tytul`, `ilosc`) VALUES ('$id', '$odpowiedz', '0')");
}
echo '<p>Dodano poprawnie! <a href="admin.php">Wróć na stronę główną</a></p>';
} else { // 5
echo '<form action="" method="POST">
<label>Tytuł:</label><br>
<input type="text" name="tytul" style="width:300px"><br>';
for($i=1;$i<=10;$i++) { // 6
echo '<label>Odpowiedź '.$i.':</label><br>
<input type="text" name="odpowiedzi[]" style="width:300px"><br>';
}
echo '<input type="checkbox" id="aktywna" name="aktywna" value="1"> <label for="aktywna">Aktywuj?</label><br>
<input type="submit" value="Wyślij">
</form>';
}
}- Jeśli jest w użyciu $_POST['tytul'] to przystępujemy do dodawania ankiety.
- Gdy zaznaczyliśmy w formularzu że ankieta ma być aktywna to wtedy wykonujemy zapytanie zmieniające wszystkie ankiety na nieaktywne. Do zmiennej $aktywna przypisujemy 1 lub 0 zależnie od wyboru.
- Wykonujemy zapytanie na dodawanie ankiety i pobieramy jej ID.
- Dla wszystkich odpowiedzi w pętli wykonujemy zapytanie na ich dodawanie. Przed tym sprawdzamy czy wpisaliśmy jakąś nazwę funkcją empty().
- Jeśli nie wcisnęliśmy przycisku Wyślij to wyświetlamy formularz.
- W pętli wyświetlamy 10 odpowiedzi. Jeśli potrzebujemy więcej można zwiększyć tą liczbę.
No i mamy dodawanie ankiety, teraz czas na edytowanie:
function admin_edytuj($id) {
$id = (int)$id;
if(isset($_POST['tytul'])) {
$tytul = $this->czysc($_POST['tytul']);
if($_POST['aktywna']=='1') {
$aktywna = '1';
mysql_query("UPDATE ankiety SET aktywna = '0'");
} else $aktywna = '0';
$result = mysql_query("UPDATE ankiety SET tytul = '$tytul', aktywna = '$aktywna' WHERE id = '$id'");
foreach($_POST['odpowiedzi'] as $id_odp => $odpowiedz) {
if(!empty($odpowiedz)) mysql_query("UPDATE odpowiedzi SET tytul = '$odpowiedz' WHERE id = '$id_odp'");
}
echo '<p>Zaktualizowano poprawnie! <a href="admin.php">Wróć na stronę główną</a></p>';
} else {
$result = mysql_query("SELECT * FROM ankiety WHERE id = '$id'");
$row = $this->stripslashes_deep( mysql_fetch_array($result) );
echo '<form action="" method="POST">
<label>Tytuł:</label><br>
<input type="text" name="tytul" style="width:500px" value="'.$row['tytul'].'"><br>';
$result2 = mysql_query("SELECT * FROM odpowiedzi WHERE id_ankiety = '$id'");
$i = 1;
while($row2 = mysql_fetch_array($result2)) {
echo '<label>Odpowiedź '.$i.':</label><br>
<input type="text" name="odpowiedzi['.$row2['id'].']" style="width:300px" value="'.$this->stripslashes_deep($row2['tytul']).'"><br>';
$i++;
}
echo '<input type="checkbox" id="aktywna" name="aktywna" value="1"'.(($row['aktywna']) ? ' checked' : '').'> <label for="aktywna">Aktywuj?</label><br>
<input type="submit" value="Wyślij">
</form>';
}
}Praktycznie wszystko tutaj jest takie samo jak przy dodawaniu z tym że nie używamy zapytania INSERT ale UPDATE. Myślę, że nic nie wymaga tutaj komentarza ;) Jedyna uwaga to taka że wyświetlamy jedynie istniejące odpowiedzi. Nie można więc dodawać ani ich usuwać - nie oszukuje się wtedy wyników ankiety. To służy raczej do poprawek błędów.
Teraz jeszcze tylko szybka funkcja na usuwanie ankiety:
function admin_usun($id) {
$result = mysql_query("DELETE FROM ankiety WHERE id = '$id'");
$result2 = mysql_query("DELETE FROM odpowiedzi WHERE id_ankiety = '$id'");
$result3 = mysql_query("DELETE FROM glosy WHERE id_ankiety = '$id'");
if($result && $result2 && $result3) {
echo '<p>Pomyślnie usunięto ankietę! <a href="admin.php">Wróć do strony głównej</a>.</p>';
}
}Funkcja bardzo prosta i nie wymaga dłuższego komentarza bo wykonuje jedynie zapytania do bazy danych :) Usuwa ankietę, odpowiedzi i głosy na podstawie ID ankiety.
No i to by było na tyle. Jeszcze tylko funkcje pomocnicze czysc() i stripslashes_deep()
function czysc($tresc) {
$tresc = mysql_real_escape_string($tresc);
return $tresc;
}
function stripslashes_deep($value) {
$value = is_array($value) ?
array_map(Array($this, 'stripslashes_deep'), $value) :
stripslashes($value);
return $value;
}Funkcja czysc() to tak jakby alias dla funkcji mysqlrealescape_string(). Istnieje ona ponieważ ma krótszą nazwę oraz zawsze możemy coś jeszcze dodać zmieniając kod tylko w jednym miejscu.
Co do stripslashesdeep() to jeśli $value jest tablicą to używamy na niej funkcji arraymap (dla każdej wartości w tablicy zostanie wywołana funkcja w pierwszym parametrze funkcji). Jeśli $value nie jest tablicą to używamy zwykłego stripslashes. Funkcja została zaczerpnięta z manuala pod funkcją stripslashes.
Klasę mamy już skończoną, teraz trzeba napisać odpowiedni kod który ją wykorzysta.
Na początek plik odpowiadający za wyświetlanie ankiety:
<?php
ob_start();
?>
<html>
<head>
<title>Ankieta</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<style type="text/css">
#ankieta {
width: 300px;
padding: 5px;
}
#ankieta .pasek {
height: 8px;
background-color: #9AC2E8;
}
#ankieta .odpowiedz {
background-color: #F0F0F0;
margin: 5px 0;
width: 100%;
padding: 5px;
}
#ankieta span.ilosc {
font-size: 12px;
margin-left: 8px;
}
#ankieta h3 {
font-size: 16px;
margin: 2px 0;
}
</style>
</head>
<body>
<?php
include "ankiety.class.php";
$ankiety = new Ankiety;
$ankiety->pokaz();
?>
</body>
</html>
<?php
ob_end_flush();
?>Na początek włączamy buforowanie abyśmy mogli wysyłać ciasteczka. Tworzymy standardowy kod HTML z kodowaniem UTF-8 i dodajemy jakieś style CSS. W środku załączamy plik z klasą, tworzymy nowy obiekt i wywołujemy funkcję pokaz().
Teraz plik admin.php:
<?php session_start(); ?>
<html>
<head>
<title>Panel administratora</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
</head>
<body>
<?php
include "ankiety.class.php";
echo '<a href="admin.php"><h3>Panel administratora</h3></a>';
$ankiety = new Ankiety;
$ankiety->admin();
?>
</body>
</html>Kod bardzo prosty: na początek startujemy sesję a w środku strony tworzymy nowy obiekt Ankiety i wywołujemy funkcję admin().
No i to by było na tyle ;) Od teraz mamy już system ankiet!
Jeszcze tylko pokażę jak to wygląda w praktyce:
