Icono del sitio Tursos

Como Hacer un Sistema de Encuestas con PHP y MySQL

Actualizado el 25-09-2020: se han cambiado todas las funciones mysql (deprecadas) por mysqli, tambien se actualizo un poco el CSS y se arreglo la URL del .zip al código

En este tutorial aprenderas a hacer un sistema de encuestas funcional, práctico y expandible, usaremos PHP y MySQL con tablas relacionales.

Trabajaremos con 4 ficheros PHP, uno para la conexión a la base de datos (conexion.php), uno para listar las encuestas (index.php), otro para crear nuevas encuestas (agregar.php), el que mostrara los resultados de las encuestas (resultado.php) y por ultimo estilos.css que se encargara de darle algo de estetica pero tambien de una parte muy esencial que es la de mostrar la barra de votaciones.

Si en ciertas partes el código te resulta muy complejo, lee los comentarios.

0. estilos.css

.barra{
    background: #ff3019;
    background: linear-gradient(to bottom, #ff3019 0%,#cf0404 100%);
    clear: both;
    height: 15px;
    color: white;
    font-weight: bold;
    text-align: right;
    padding: 6px;
    border-radius: 4px;
    max-width: 356px;
    min-width: 20px;
}
 
/* A partir de aqui es solo cosmetico, lo puedes borrar */
*{
    margin: 0;
    padding: 0;
    font-family: sans-serif;
    font-size: 14px;
}
body{
    background: #464646;
}
a{
    text-decoration: none;
    color: red;
}
.fr{
    float: right;
}
.fl, .votar{
    float: left;
}
.cf{
    clear: both;
}
.wrap{
    padding: 25px;
    margin: 0 auto;
    width: 385px;
    background: #F3F3F3;
    border-radius: 4px;
    margin-top: 70px;
    border: 1px solid #E0E0E0;
}
h1{
    margin-bottom: 20px;
    font-size: 21px;
    border-bottom: 1px solid #DDD;
    padding-bottom: 15px;
    text-align: center;
}
form > div{
    margin-bottom: 20px;
}
form .titulo{
    margin-right: 20px;
}
form label{
    display: block;
}
form input[type='text'], select{
    border: 1px solid #E0E0E0;
    padding: 6px;
    resize: none;
}
form input[type="submit"]{
    padding: 8px 16px;
    background: gray;
    border: 0;
    display: block;
    font-weight: bold;
    color: white;
    border-radius: 6px;
}
form input[type="submit"]:hover{
    background: #505050;
}
ul.votacion{
    margin-bottom: 25px;
    list-style: none;
}
.votacion li a{
    padding: 8px;
    background: #FCFCFC;
    border-radius: 5px;
    margin-bottom: 5px;
    color: gray;
    font-weight: bold;
    font-size: 16px;
    display: block;
}
.votacion li a:hover{
    color: black;
    background: white;
}
.votacion li span{
    margin-left: 10px;
}
.votacion .fl{
    margin-bottom: 5px;
}
.volver{
    display: block;
    padding-top: 15px;
    clear: both;
}
.votar{
    float: left;
}
.resultado{
    float: left;
    margin-left: 10px;
    margin-top: 7px;
    color: black;
}

1. SQL

Creamos una tala con el nombre encuestas con los siguientes campos: id, titulo y fecha. Aqui se almacenara los datos las encuestas como el titulo y la fecha que fue creada la encuestas, utilizaremos otra tabla para que almacene las opciones de las encuestas.

CREATE TABLE IF NOT EXISTS `encuestas` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`titulo` varchar(50) NOT NULL,
`fecha` date NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;

Ahora insertarmos unos datos de ejemplo en la tabla:

INSERT INTO `encuestas` (`id`, `titulo`, `fecha`)
VALUES (NULL ,  'Que os parecen los tutoriales de PHP?',
'2012-12-15');

Creamos otra tabla con el nombre opciones con los siguientes campos: id, id_encuesta, nombre y valor.
El campo de id_encuesta tendrá relación con el campo id de la tabla encuestas, en nombre ira el nombre y valor guardara el numero de votos que tendra la opcion respectiva.

CREATE TABLE IF NOT EXISTS `opciones` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `id_encuesta` int(11) NOT NULL,
  `nombre` varchar(50) NOT NULL,
  `valor` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;

Insertamos datos a la tabla opciones:

INSERT INTO  `opciones` (`id` ,`id_encuesta` ,`nombre` ,`valor`)
VALUES (NULL ,  '1',  'Muy Bueno',  '14')
, (NULL ,  '1',  'Bueno',  '6')
, (NULL ,  '1',  'Malo',  '2');

Fijaos que he definido id_encuesta con 1 y es porque quiero que estas opciones esten relacionadas con la encuestas que creamos anteriormente que llevaba el id de 1.

Si no entendisteis bien como vamos a trabajar las relaciones de las tablas, esta grafica te aclarará las dudas:

02. conexion.php

El tipico fichero PHP para guardar los datos de la conexión, nada del otro mundo:

<?php // datos para la conexion a mysql
define('DB_SERVER','localhost');
define('DB_NAME','tursos');
define('DB_USER','root');
define('DB_PASS','root');
 
$conex = mysqli_connect(DB_SERVER,DB_USER,DB_PASS);
mysqli_select_db($conex, DB_NAME);

03. index.php

Lo primero sera incluir al fichero de conexión, luego haremos una consulta a la base datos para que nos muestren los títulos de la tabla ‘encuestas’ los mismos que mostraremos como enlaces hacia las encuestas, estos enlaces llevaran el id de la encuestas en cuestión mediante GET (encuestas.php?id=x).

<?php
require('conexion.php');
$sql = "SELECT * FROM encuestas ORDER BY id DESC";
$req = mysqli_query($conex, $sql);
?>
<!DOCTYPE HTML>
<html lang="en-US">
<head>
    <meta charset="UTF-8">
    <title>Sistema de encuestas</title>
    <link rel="stylesheet" href="estilos.css">
</head>
<body>
    <div class="wrap">
        <h1>Encuestas</h1>
        <ul class="votacion index">
        <?php
            while($result = mysqli_fetch_object($req)){
                echo '<li><a href="encuesta.php?id='.$result->id.'">'.$result->titulo.'</a></li>';
            }
        ?>
        </ul>
        <a href="agregar.php">+ Agregar nueva encuesta</a>
    </div>
</body>
</html>

Nos debería ir quedando así:

04. encuesta.php

Linea 04: Si no existe la variable de URL se regresara a la pagina principal.
Linea 11: Si le dan a votar (el form de votacion ira abajo) y si han seleccionado una opcion se va a actualizar los datos del campo ‘valor’ de la tabla ‘opciones’:

<?php
require('conexion.php');
    $id = $_GET['id'];
    if(!isset($_GET['id'])){
        header('location: index.php');
    }
 
    if(isset($_POST['votar']))
    {
 
        if(isset($_POST['valor'])){
            $opciones = $_POST['valor'];
            $mod = mysqli_query($conex, "SELECT * FROM opciones WHERE id = ".$opciones);
            while($result = mysqli_fetch_object($mod)){
                $valor = $result->valor + 1; // obtenemos el valor de 'valor' y le añadimos 1 unidad
                mysqli_query($conex,"UPDATE opciones SET valor =  '".$valor."' WHERE id = ".$opciones); // luego ejecutamos el query SQL
            }
            header('location: resultado.php?id='.$id); // Por ultimo lo redireccionamos a la encuestas mostrando los resultados.
        }
    }
?>

Ahora la parte HTML donde ira el form de votacion, aqui vamos a generar una consulta a la base datos para que se muestren los datos de la tabla ‘encuestas’ con relación a la tabla ‘opciones’ como radiobuttons, partes de un form:

Linea 15: En esta linea haremos un query con INNER JOIN en el cual trabajaremos con las tablas ‘encuestas’ y ‘opciones’ donde a sera encuestas y b es opciones, aqui buscaremos filas donde a.id sea igual a b.encuesta_id, el valor de a.id que se comparara sera definido por $id, este valor lo obtuvimos al principio en el primer pedazo de código mediante GET.

Linea 18 y 27: Con el resultado del query hacemos un while donde mostraremos el titulo de la encuesta y luego en la linea 48 se muestran cada una de las opciones como input[type=’radio’], $aux es una variable auxiliar que nos ayuda a detener el bucle para que ciertos elementos se muestren solo una vez.

<!DOCTYPE HTML>
<html lang="en-US">
<head>
    <meta charset="UTF-8">
    <title>Sistema de encuestas</title>
    <link rel="stylesheet" href="estilos.css">
</head>
<body>
 
<div class="wrap">
 
<form action="" method="post">
<?php
$aux = 0;
$sql = "SELECT a.titulo as titulo, a.fecha as fecha, b.id as id, b.nombre as nombre, b.valor as valor FROM encuestas a INNER JOIN opciones b ON a.id = b.id_encuesta WHERE a.id = ".$id;
$req = mysqli_query($conex, $sql);
 
while($result = mysqli_fetch_object($req)){
 
    if($aux == 0){
        echo '<h1>'.$result->titulo.'</h1>';
 
        echo '<ul class="votacion">';
        $aux = 1;
    }
 
    echo '<li><label><input name="valor" type="radio" value="'.$result->id.'"><span>'.$result->nombre.'</span></label></li>';
 
}
    echo '</ul>'; 
 
    if(!isset($_POST['valor'])){
        echo "<div class='error'>Selecciona una opcion.</div>";
    }
 
    echo '<input name="votar" type="submit" value="Votar" class="votar">';
    echo '<a href="resultado.php?id='.$id.'" class="resultado">Ver Resultados</a>';
    echo '<a href="index.php" class="volver">← Volver</a>';
 
?>
 
</form>
</div>
 
</body>
</html>

Si todo salió bien, encuesta.php deberia verse asi:

05. resultado.php

Esta archivo es mas simple, aqui mostraremos los resultados de las encuestas de acuerdo al id obtenido por GET (resultado.php?id=x):
Linea 11: Para obtener el valor usamos SUM() para que nos resulte la suma total del valor en un solo numero:

<?php
 
require('conexion.php');
 
if(!isset($_GET['id'])){
    header('location: index.php');
}
 
$suma = 0;
$id = $_GET['id'];
$mod = mysqli_query($conex,"SELECT SUM(valor) as valor FROM opciones WHERE id_encuesta = ".$id);
while($result = mysqli_fetch_object($mod)){
    $suma = $result->valor;
}
 
?>

Luego mostramos los resultados con el mismo query con INNER JOIN que ya hemos usado anteriormente, esto nos mostrara cada opción en forma de div (.barra) al que le definimos el ancho en porcentaje usando una operación matemática:

<!DOCTYPE HTML>
<html lang="en-US">
<head>
    <meta charset="UTF-8">
    <title>Sistema de Encuestas</title>
    <link rel="stylesheet" href="estilos.css">
</head>
<body>
<div class="wrap">
<form action="" method="post">
<?php
$aux = 0;
$sql = "SELECT a.titulo as titulo, a.fecha as fecha, b.id as id, b.nombre as nombre, b.valor as valor FROM encuestas a INNER JOIN opciones b ON a.id = b.id_encuesta WHERE a.id = ".$id;
$req = mysqli_query($conex, $sql);
 
while($result = mysqli_fetch_object($req)){
    if($aux == 0){
            echo "<h1>".$result->titulo."</h1>";
            echo "<ul class='votacion'>";
        $aux = 1;
    }
    echo '<li><div class="fl">'.$result->nombre.'</div><div class="fr">Votos: '.$result->valor.'</div>';
    if($suma == 0){
        echo '<div class="barra cero" style="width:0%;"></div></li>';
    }else{
        echo '<div class="barra" style="width:'.($result->valor*100/$suma).'%;">'.round($result->valor*100/$suma).'%</div></li>';
    }
 
}
echo '</ul>'; 
 
if(isset($aux)){
    echo '<span class="fr">Total: '.$suma.'</span>';
    echo '<a href="encuesta.php?id='.$id.'"" class="volver">← Volver</a>';
}
 
?>
</ul>
</form>
</div>
</body>
</html>

Si llegaste hasta aqui resultado.php debería lucir similar a esto:

06. agregar.php

En este fichero es probablemente el mas complejo de todos, manejaremos el proceso PHP arriba y el formulario abajo, por ello para entender esta parte es necesario ver el código completo (leer comentarios):

<?php require('conexion.php');
$cont = 0;
 
$titulo = ''; if(isset($_POST['titulo'])){ $titulo = trim($_POST['titulo']); } // definimos $titulo para evitar errores, y guardamos su valor por el ingresado.
 
if(isset($_POST['enviar'])){
     if($titulo != ""){
        $num = $_POST['opciones']; // este valor lo vamos a obtener de lo que el usuario ingrese como numero de opciones al crear la encuesta
        $fecha = date('Y-m-d');
 
        $sql= "INSERT INTO `encuestas` (`id` ,`titulo` ,`fecha`) VALUES (NULL ,  '$titulo', '$fecha');"; // si han ingresado si quiera un titulo insertamos esta encuesta en la tabla
        mysqli_query($conex, $sql);
 
        $sql = "SELECT MAX(id) as id FROM encuestas"; // ahora obtenemos el id de la ultima fila,
                                                      // la que acabamos de ingresar,
                                                      // esto lo hacemos para poder asociarle las opciones
        $req =  mysqli_query($conex, $sql);
 
        while($result = mysqli_fetch_object($req)){
            $id_encuesta = $result->id;  // con el resultado obtenido hacemos un bucle y definimos los resultados como id_encuesta.
        }
 
        $sql = "INSERT INTO  `opciones` (`id` ,`id_encuesta` ,`nombre` ,`valor`) VALUES "; // En esta parte estamos armando un query SQL dinamico el cual sera modificado de acuerdo a lo que el usuario ingrese en el formulario.
        for($i=1;$i<=$num;$i++){
            $opcnativa = trim($_POST['opc'.$i]); // obtenemos el nombre de cada opcion indivudalmente.
            if($opcnativa != ""){
                $sql .= "(NULL ,  '$id_encuesta',  '$opcnativa',  '0')"; // el id de la opcion ira null para que se ponga automaticamente, en id_encuesta pues ira el id de la encuesta que acabamos de crear, en 'nombre' ira el nombre de la opcion y valor ira 0, puesto que es una nueva opcion sin votos, esto se repetira con todas las opciones que el usuario haya definido.
                $cont++;
            }
            if($i == $num){
                $sql .= ";"; // si es que se llega al final, termina la consulta
            }else{
                $sql .= ", "; // sino se pone una , y se continua.
            }
        }
 
        if($cont < 2){ // si el usuario no definio ninguna opcion, se elimina la encuesta recien creada, esto es poco probable que suceda ya que la definicion de opciones la haremos con un select, y aqui se seleccionara el valor de 2 por defecto.
            $sql = "DELETE FROM `encuestas` WHERE id = ".$id_encuesta;
            echo "<div class='error'>Tiene que llevar por lo menos 2 opciones.</div>";
        }else{
            header('location: index.php'); // por ultimo si todo salio bien, redireccionamos al index para que el usuario vea su encuesta recien creada.
        }
        mysqli_query($conex, $sql); // y ejecutamos el query
    }
}
?>
<!DOCTYPE HTML>
<html lang="en-US">
<head>
    <meta charset="UTF-8">
    <title>Sistema de Encuestas</title>
    <link rel="stylesheet" href="estilos.css">
</head>
<body>
 
<div class="wrap">
    <h1>Agregar Encuesta</h1>
    <form action="" method="post">
 
    <div class="fl titulo">
        <label>Titulo:</label>
        <input name="titulo" type="text" value="<?php echo $titulo; ?>" size="26">
    </div>
    <?php
        // esto es simplemente un formulario, pero aqui hacemos una condicion, identificamos si se ha definido un numero de opciones, si es si hacemos un bucle, si es no mostramos el select para definir un numero de opciones, como es obvio por defecto se mostrara el bucle:
    if(isset($_POST['opc'])){
        $num = $_POST['opciones']; // guardamos el valor del numero de opciones
        for($i=1;$i<=$num;$i++){ // hacemos el bucle mostrando los campos respectivos.
    ?>
    <div class="cf">
        <label>Opcion <?php echo $i; ?>: </label>
        <input name="opc<?php echo $i; ?>" type="text" size="43">
    </div>
    <?php } // aqui termina el bucle ?>
    <div class="cf">
        <input name="enviar" type="submit" value="Enviar">
        <input name="opciones" type="hidden" value="<?php echo $num; // le pasamos el valor de num al proceso del formulario mediante un campo oculto. ?>">
        <input name="cont" type="hidden" value="<?php echo cont; ?>">
    </div>
    <?php }else{ // sino se ha definido nro de opciones: ?>
    <div class="fl">
        <label>Nº de opciones:</label>
        <select name="opciones">
            <?php for($i=2;$i<=20;$i++){ // esto es un loop simple, solo para ahorrarnos trabajo, este select tendra de 2 a 20 opciones, si deseas cambiarlo lo puedes hacer aqui. ?>
            <option value="<?php echo $i; ?>"><?php echo $i; ?></option>
            <?php } ?>
        </select>
    </div>
 
    <div class="cf">
        <input name="opc" type="submit" value="Continuar">
    </div>
 
      <?php } // Sino se han definido opciones, que en vez de salir el boton de Enviar, salga uno que sea Continuar. ?>
    <a href="index.php" class="volver">← Volver</a>
    </form>
    </div>
</body>
</html>

Por ultimo, agregar.php en sus dos fases:


Posibles mejoras

Dentro de las posibles mejoras que se le podemos hacer al sistema, esta la creación de nuevas encuestas (agregar.php) detrás de un login, otra adición seria la de impedir votaciones consecutivas bloqueando la IP o por cookies.

Si no entendiste alguna parte o te sale algún error, házmela saber en los comentarios y recuerda que puedes bajarte el código fuente desde este enlace.