Los sistemas de encuestas se utilizan principalmente para conocer la opinión de los usuarios de una web. Existen muchos servicios disponibles que te proporcionan un sistema integrado de encuestas, pero si quieres crear tu propio sistema de votación, puede ser que este tutorial te interese bastante.
En este artÃculo te ayudaremos a desarrollar un script para crear un sistema de encuestas simple mediante PHP. Utilizaremos tanto PHP, como MySQL para crear el sistema de encuestas y, por muy extraño que te parezca, no utilizaremos ni Javascript ni jQuery. Además, nuestro sistema de encuestas almacenará los votos de los usuarios en una base de datos, la cual utilizaremos después para representar la gráfica con las opiniones vertidas por los usuarios. Te explicamos como implementaremos esto detalladamente a continuación.
Como te he comentado antes, almacenaremos los votos de los usuarios en la base de datos MySQL. La pregunta de la encuestas y las diferentes opciones también se extraerán de la base de datos para mostrárselas a los distintos usuarios. El usuario podrá seleccionar una opción y enviar su voto. Una vez el voto se ha enviado, se almacenará en la base de datos con la opción que ha seleccionado de la respectiva encuesta. También, usaremos cookies de PHP para impedir que el usuario pueda votar en repetidas ocasiones. El resultado de la encuesta con el total de votos y los votos de cada opción se mostrarán en la página de resultados. Dichos resultados serán representados con un diagrama de barras.
Creación de la base de datos
Para crear nuestro sistema de encuestas utilizaremos 3 tablas: polls, poll_options y poll_votes.
La tabla polls contendrá la pregunta o el asunto de la encuesta.
CREATE TABLE `polls` ( `id` int(11) NOT NULL AUTO_INCREMENT, `subject` varchar(255) COLLATE utf8_unicode_ci NOT NULL, `created` datetime NOT NULL, `modified` datetime NOT NULL, `status` enum('1','0') COLLATE utf8_unicode_ci NOT NULL DEFAULT '1', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
La tabla poll_options contendrá las diversas opciones de cada una de las encuestas.
CREATE TABLE `poll_options` ( `id` int(11) NOT NULL AUTO_INCREMENT, `poll_id` int(11) NOT NULL, `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL, `created` datetime NOT NULL, `modified` datetime NOT NULL, `status` enum('1','0') COLLATE utf8_unicode_ci NOT NULL DEFAULT '1', PRIMARY KEY (`id`), KEY `poll_id` (`poll_id`), CONSTRAINT `poll_options_ibfk_1` FOREIGN KEY (`poll_id`) REFERENCES `polls` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
La tabla poll_votes almacerá el contador de votos de las opciones de las encuestas.
CREATE TABLE `poll_votes` ( `id` int(11) NOT NULL AUTO_INCREMENT, `poll_id` int(11) NOT NULL, `poll_option_id` int(11) NOT NULL, `vote_count` bigint(10) NOT NULL, PRIMARY KEY (`id`), KEY `poll_id` (`poll_id`), KEY `poll_option_id` (`poll_option_id`), CONSTRAINT `poll_votes_ibfk_1` FOREIGN KEY (`poll_id`) REFERENCES `polls` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT `poll_votes_ibfk_2` FOREIGN KEY (`poll_option_id`) REFERENCES `poll_options` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
Creamos una nueva encuesta y sus respectivas opciones:
INSERT INTO `polls` (`id`, `subject`, `created`, `modified`, `status`) VALUES (1, '¿Cuál es tu mascota preferida?', '2016-11-07 04:13:13', '2016-11-07 04:13:13', '1');
INSERT INTO `poll_options` (`id`, `poll_id`, `name`, `created`, `modified`, `status`) VALUES (1, 1, 'Perro', '2016-11-07 11:29:31', '2016-11-07 11:29:31', '1'), (2, 1, 'Gato', '2016-11-07 11:29:31', '2016-11-07 11:29:31', '1'), (3, 1, 'Hamster', '2016-11-07 11:29:31', '2016-11-07 11:29:31', '1'), (4, 1, 'Otro', '2016-11-08 08:20:25', '2016-11-08 08:20:25', '1');
Clase Poll
Utilizaremos la clase poll para gestionar todo lo relacionado con la encuesta y el sistema de votación. La clase cuenta con estos métodos:
- __construct(): Conecta con la base de datos
- getQuery(): Ejecuta las queries SQL en la base de datos MySQL y devuelve los respectivos datos. Es una función privada que solo se puede utilizar dentro de la clase
- getPolls(): Obtiene la encuesta y sus respectivas opciones.
- vote(): Inserta o modifica el contador de votos en la base de datos
- getResult(): Proporciona el resultado de la encuesta con su contador de votos de la encuesta y de las opciones.
<?php /* * Poll Management Class * This class is used to manage the online poll & voting system * @author CodexWorld.com * @url http://www.codexworld.com * @license http://www.codexworld.com/license */ class Poll{ private $dbHost = 'localhost'; private $dbUser = 'root'; private $dbPwd = ''; private $dbName = 'poll_system'; private $db = false; private $pollTbl = 'polls'; private $optTbl = 'poll_options'; private $voteTbl = 'poll_votes'; public function __construct(){ if(!$this->db){ // Connect to the database $conn = new mysqli($this->dbHost, $this->dbUser, $this->dbPwd, $this->dbName); if($conn->connect_error){ die("Failed to connect with MySQL: " . $conn->connect_error); }else{ $this->db = $conn; } } } /* * Runs query to the database * @param string SQL * @param string count, single, all */ private function getQuery($sql,$returnType = ''){ $data = ''; $result = $this->db->query($sql); if($result){ switch($returnType){ case 'count': $data = $result->num_rows; break; case 'single': $data = $result->fetch_assoc(); break; default: if($result->num_rows > 0){ while($row = $result->fetch_assoc()){ $data[] = $row; } } } } return !empty($data)?$data:false; } /* * Get polls data * Returns single or multiple poll data with respective options * @param string single, all */ public function getPolls($pollType = 'single'){ $pollData = array(); $sql = "SELECT * FROM ".$this->pollTbl." WHERE status = '1' ORDER BY created DESC"; $pollResult = $this->getQuery($sql, $pollType); if(!empty($pollResult)){ if($pollType == 'single'){ $pollData['poll'] = $pollResult; $sql2 = "SELECT * FROM ".$this->optTbl." WHERE poll_id = ".$pollResult['id']." AND status = '1'"; $optionResult = $this->getQuery($sql2); $pollData['options'] = $optionResult; }else{ $i = 0; foreach($pollResult as $prow){ $pollData[$i]['poll'] = $prow; $sql2 = "SELECT * FROM ".$this->optTbl." WHERE poll_id = ".$prow['id']." AND status = '1'"; $optionResult = $this->getQuery($sql2); $pollData[$i]['options'] = $optionResult; } } } return !empty($pollData)?$pollData:false; } /* * Submit vote * @param array of poll option data */ public function vote($data = array()){ if(!isset($data['poll_id']) || !isset($data['poll_option_id']) || isset($_COOKIE[$data['poll_id']])) { return false; }else{ $sql = "SELECT * FROM ".$this->voteTbl." WHERE poll_id = ".$data['poll_id']." AND poll_option_id = ".$data['poll_option_id']; $preVote = $this->getQuery($sql, 'count'); if($preVote > 0){ $query = "UPDATE ".$this->voteTbl." SET vote_count = vote_count+1 WHERE poll_id = ".$data['poll_id']." AND poll_option_id = ".$data['poll_option_id']; $update = $this->db->query($query); }else{ $query = "INSERT INTO ".$this->voteTbl." (poll_id,poll_option_id,vote_count) VALUES (".$data['poll_id'].",".$data['poll_option_id'].",1)"; $insert = $this->db->query($query); } return true; } } /* * Get poll result * @param poll ID */ public function getResult($pollID){ $resultData = array(); if(!empty($pollID)){ $sql = "SELECT p.subject, SUM(v.vote_count) as total_votes FROM ".$this->voteTbl." as v LEFT JOIN ".$this->pollTbl." as p ON p.id = v.poll_id WHERE poll_id = ".$pollID; $pollResult = $this->getQuery($sql,'single'); if(!empty($pollResult)){ $resultData['poll'] = $pollResult['subject']; $resultData['total_votes'] = $pollResult['total_votes']; $sql2 = "SELECT o.id, o.name, v.vote_count FROM ".$this->optTbl." as o LEFT JOIN ".$this->voteTbl." as v ON v.poll_option_id = o.id WHERE o.poll_id = ".$pollID; $optResult = $this->getQuery($sql2); if(!empty($optResult)){ foreach($optResult as $orow){ $resultData['options'][$orow['name']] = $orow['vote_count']; } } } } return !empty($resultData)?$resultData:false; } }
Vista de la encuesta (index.php)
Este fichero muestra la pregunta de la encuesta y sus distintas opciones. Para que el usuario pueda seleccionar una opción, cuenta con un radio button. Cuando el usuario quiera enviar su votación, debe hacer clic en el botón submit que hay en la parte inferior.
<?php //Get poll and options data $pollData = $poll->getPolls(); ?> <div class="pollContent"> <?php echo !empty($statusMsg)?'<p class="stmsg">'.$statusMsg.'</p>':''; ?> <form action="" method="post" name="pollFrm"> <h3><?php echo $pollData['poll']['subject']; ?></h3> <ul> <?php foreach($pollData['options'] as $opt){ echo '<li><input type="radio" name="voteOpt" value="'.$opt['id'].'" >'.$opt['name'].'</li>'; } ?> </ul> <input type="hidden" name="pollID" value="<?php echo $pollData['poll']['id']; ?>"> <input type="submit" name="voteSubmit" class="button" value="Vote"> <a href="results.php?pollID=<?php echo $pollData['poll']['id']; ?>">Results</a> </form> </div>
Una vez que el voto ha sido enviado, se inserta en la BD mediante la clase Poll. Definimos también una cookie para que el usuario no pueda votar en repetidas ocasiones.
<?php //Include and initialize Poll class include 'Poll.php'; $poll = new Poll; //Check whether vote is submitted if(isset($_POST['voteSubmit'])){ $voteData = array( 'poll_id' => $_POST['pollID'], 'poll_option_id' => $_POST['voteOpt'] ); //Submit vote by Poll class $voteSubmit = $poll->vote($voteData); if($voteSubmit){ //store in $_COOKIE to signify the user has voted setcookie($_POST['pollID'], 1, time()+60*60*24*365); $statusMsg = 'Your vote has been submitted successfully.'; }else{ $statusMsg = 'Your vote already had submitted.'; } } ?>
Resultado de la encuesta (results.php)
En este archivo, extraemos los resultados de la encuesta de la base de datos y se los mostramos al usuario. El contador de votos de cada opción lo convertimos en un porcentaje que aparece en cada una de las barras.
<?php //Include and initialize Poll class include 'Poll.php'; $poll = new Poll; ?> <?php //Get poll result data $pollResult = $poll->getResult($_GET['pollID']); ?> <h3><?php echo $pollResult['poll']; ?></h3> <p><b>Total Votes:</b> <?php echo $pollResult['total_votes']; ?></p> <?php if(!empty($pollResult['options'])){ $i=0; //Option bar color class array $barColorArr = array('azure','emerald','violet','yellow','red'); //Generate option bars with votes count foreach($pollResult['options'] as $opt=>$vote){ //Calculate vote percent $votePercent = round(($vote/$pollResult['total_votes'])*100); $votePercent = !empty($votePercent)?$votePercent.'%':'0%'; //Define bar color class if(!array_key_exists($i, $barColorArr)){ $i=0; } $barColor = $barColorArr[$i]; ?> <div class="bar-main-container <?php echo $barColor; ?>"> <div class="txt"><?php echo $opt; ?></div> <div class="wrap"> <div class="bar-percentage"><?php echo $votePercent; ?></div> <div class="bar-container"> <div class="bar" style="width: <?php echo $votePercent; ?>;"></div> </div> </div> </div> <?php $i++; } } ?> <a href="index.php">Back To Poll</a>
Código CSS
En el fichero index.php, coloca el siguiente código CSS para personalizar nuestra encuesta y sus resultados.
.pollContent{ float: left; width: 500px; } .pollContent h3 { font-size: 18px; color: #333; text-align: left; float: left; border-bottom: 2px solid #333; width: 100%; margin: 0 auto; padding-bottom: 10px; } .pollContent ul{ list-style: none; float: left; width: 100%; padding: 10px; } .pollContent input[type="submit"], .pollContent a{ border: none; font-size: 16px; color: #fff; border-radius: 3px; padding: 10px 15px 10px 15px; background-color: #34a853; text-decoration: none; cursor: pointer; }
En el archivo results.php añade este otro.
#container { text-align: center; margin: 20px; } h2 { color: #CCC; } a { text-decoration: none; color: #EC5C93; } .bar-main-container { margin: 10px auto; width: 300px; height: 55px; -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; font-family: sans-serif; font-weight: normal; font-size: 0.8em; color: #FFF; } .wrap { padding: 8px; } .bar-percentage { float: left; background: rgba(0,0,0,0.13); -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; padding: 9px 0px; width: 18%; height: 16px; margin-top: -15px; } .bar-container { float: right; -webkit-border-radius: 10px; -moz-border-radius: 10px; border-radius: 10px; height: 10px; background: rgba(0,0,0,0.13); width: 78%; margin: 0px 0px; overflow: hidden; } .bar-main-container .txt{ padding-top: 5px; font-size: 16px; font-weight: bold; } .bar { float: left; background: #FFF; height: 100%; -webkit-border-radius: 10px 0px 0px 10px; -moz-border-radius: 10px 0px 0px 10px; border-radius: 10px 0px 0px 10px; -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"; filter: alpha(opacity=100); -moz-opacity: 1; -khtml-opacity: 1; opacity: 1; } /* COLORS */ .azure { background: #38B1CC; } .emerald { background: #2CB299; } .violet { background: #8E5D9F; } .yellow { background: #EFC32F; } .red { background: #E44C41; }
Fuente: codexworld.com