Pablo Durán

Cuestionario JavaScript

Fecha de publicación: 29/06/20

Antes de empezar

Propósito del proyecto

Lo que pretendo conseguir con este proyecto es obtener ciertos conocimientos básicos del lenguaje JavaScript ya que es la prima vez que lo voy a usar. Tambien usaré Laravel como framework de back-end pero puedes descargarte el front-end totalmente funcional en mi github.

¿Que necesitamos?

  • Conocimientos de programación orientada a objetos
  • Un navegador
  • Un procesador de texto, en mi caso VSCode
  • Conocimientos de laravel en caso de querer hacerlo con back-end
  • (Opcional) Ciertos conocimientos de css

Explicación

Panificación

Antes de empezar un proyecto lo más importante es que le dediques un cierto tiempo a planificarlo para saber los pasos a seguir y no tener que estar borrando todo tu trabajo porque después quieres hacerlo de otra forma.

La estructura básica que he planeado para este proyecto es tener dos páginas, una que funcione como selector del tipo de cuestionario y una segunda para las preguntas y el final del cuestionario. He escogido este sistema porque guardo la información de los cuestionarios en una base de datos por lo que en la primera página selecciono el cuestionario y mediante una query pedirle los datos al servidor y mostrarlos en la segunda página.

base de datos

A la hora de planificar el proyecto pensé que sería buena idea el crear diferentes tipos de cuestionarios y no limitarme a uno solo. Por ello, no lo puedo hacer de forma estática y para tenerlo más ordenado decidí crear diversas tablas que albergasen la información y mediante consultas pedir los datos necesarios. La estructura montada es la siguiente:

estructura de la base de datos estructura de la base de datos en vertical

Si observamos la estructura creada en la imagen anterior vemos que tenemos 4 tablas diferentes. Las cuales son:

  • cuestionarioTipo: que tiene el nombre del cuestionario a realizar además de una descripción por si se quiere añadir algo al estilo tarjetas o mensajes flotantes y lo más importante el id. Esta última columna comentada es la más importante de toda la estructura ya que con ella haremos las consultas con las que obtener las preguntas y respuestas del cuestionario que queramos.
  • cuestionarioPreguntas: como su nombre indica la tabla alberga las preguntas que queramos realizar además de un identificador y una clave foránea idTipoFK el cual está vinculado con el identificado de cuestionarioTipo.
  • cuestionarioRespuestas: dispone de su identificador propio y una columna de texto para la respuesta.
  • cuestionarioEnlacePR: enlaza cada respuesta a su pregunta correspondiente teniendo una clave primaria compuesta con los identificadores de pregunta y respuesta. La conexión se tiene que hacer en una tabla a parte debido a que una pregunta tiene varias respuesta, pero también, una respuesta puede estar en varias preguntas. Ejemplo “verdadero” o “falso”. Esta tabla aún guarda un as bajo la manga, la columna RespuestaCorrecta la cual es de tipo boolean y nos dirá qué respuesta es la correcta en cada caso.

Os dejo en mi github toda la estuctura de la base de datos en archivos sql.

Selector de cuestionarios

Esta será la primera página que se encontrará la persona que quiera hacer un cuestionario. En la cual podrá elegir cual de todos los que haya en la base de datos quiere hacer.

Primero tenemos que crear el archivo que tendrá el html de la página, en mi caso se llamará quizSelector.blade.php y creamos a estructura básica que queramos mostrar.

Una vez tenemos en archivo quizSelector creado tenemos que crear un enlace para poder visualizar la página. Nos vamos a “routes>web.php” y ponemos la siguente liena Route::get('/quizselector', 'mainController@quizselector'); en el que “/quizselector” es el link que queremos que tenga para acceder a la página y el “'mainController@quizselector” es el nombre del controlado y método a utilizar.

El siguiente paso es crear el método al que llama la url que hemos creado. Para crearlo nos vamos a un controlador que tengamos en “app>Controllers>nombre del controlador” en mi caso me he creado mi propio controlado llamado mainController.php que es al que hemos llamado a la hora de crear la url. Dentro de este archivo creamos una nueva función entre las llaves de la clase del controlador. Mi función se va a llamar “quizselector” ya que así lo pusimos en la url. Y en su interior llamaremos mediante el constructor de consultas a los nombres e identificadores de la table “cuestionarioTipo” quedando algo parecido a esto $tipos =\DB::table('cuestionarioTipo')->get(); . Como puedes observar la respuesta de la consulta la he guardado en una variable llamada $tipos, es importante recordar esto más adelante. Simplemente nos queda crear el return para que nos devuelva la vista que hemos quedado al principio, para ello, en el return llamamos al método view y le decimos que nos abra la página “quizSelector” sin ponerle el .blade.php, también le pasamos la variable que os he dicho para poder visualizarlo en la página mediante el método compact. Todo en su conjunto quedaría así:

                                
class mainController extends Controller
{
    public function quizselector(){

        $tipos =\DB::table('cuestionarioTipo')->get();

        return view ("quizSelector", compact("tipos"));
    }
}
                                
                            

Una vez tenemos el objeto con los nombres e identificadores de cada cuestionario solo nos falta mostrarlo en la página. Para mostrar los datos usaremos un bucle foreach en el que generará un botón dentro de un formulario por cada cuestionario que tengamos. Lo único que necesitamos poner en el interior del botón en el id en el valor del botón y el nombre como texto a mostrar. Si usas "blade" de Laravel quedaría algo así:

                                
<form id="selector" method="post" action="/quiz">
            
    
    @foreach($tipos as $tipo)
        <p>
            <button id="{{$tipo->id}}" name="id" value="{{$tipo->id}}" type="submit" class="btn btn-outline-primary btn-block">{{$tipo->nombre}}</button>
            
        </p>
    @endforeach

</form>
                                
                            

Consultas SQL

Una vez le damos a uno de los botones para seleccionar que cuestionario queremos tenemos que recoger esa información para saber que preguntas y respuestas tenemos que recoger. Si obserbas el botón que hemos creado anteriormente con el nombre de cada cuestionario es de tipo submit por lo que una vez le clicamos enviará los datos a donde le hayamos dicho, en nuestro caso al link "/quiz" el cual hará las consultas necesarias y nos mostrará las preguntas para poder hacer el test.

Como aún no tenemos creado el link "/quiz" es lo próximo que vamos a hacer y es parecido a como lo hemos hecho antes. No vamos a "routes>web" y escribimos la siguiente linea de código Route::post('/quiz', 'inputController@quiz'); Hay dos cosas a tener en cuenta, la primera y más importante es que la ruta es de tipo post ya que así se lo hemos dicho en el formulario que habíamos creado. Lo segundo, es que en mi caso uso otro controlador pero simplemente para tener todo más ordenado pero tú puedes ponerlo en el mismo controlador que el link "/quizselector" que hemos hecho anteriormente.

Creamos el método en cuestión al que yo he llamado "quiz". En los paréntesis de la función le pedimos con entrada los datos del formulario de esta forma quiz(Request $request) pasa saber a que cuestionario le tenemos que pedir las cosas. Ahora viene una de las cosas más complicadas, las consultas SQL. Lo divideremos en cuatro consultas diferentes y lo guardaremos cada una en una variable diferente.

  • $tipos: Solo queremos el nombre del cuestionario para ponerlo como título creando la siguiente consulta: $tipos =\DB::select("SELECT nombre FROM `cuestionarioTipo` WHERE `id` = " . $request->id); donde el $request->id es el id del cuestionario del que queremos los datos
  • $preguntas: Queremos que nos devuelva el id de la pregunta y el texto de este. Para ello usamos la siguiente consulta: $preguntas = \DB::select("SELECT cuestionarioPreguntas.pregunta, cuestionarioPreguntas.id FROM cuestionarioPreguntas WHERE cuestionarioPreguntas.idTipoFK = ". $request->id); .
  • $respuestas: En esta consulta pediremos el id de la pregunta, el id de la respuesta y el texto de la respuesta en cuestión. Como no podemos tener dos columnas con el mismo nombre en el objeto, añadimos en la consulta AS 'idPregunta' para el id de la pregunta y AS 'idRespuesta' para el id de la respuesta quedamos así:$respuestas = \DB::select("SELECT cuestionarioPreguntas.id AS 'idPregunta', cuestionarioRespuestas.id AS 'idRespuesta', cuestionarioRespuestas.respuesta FROM cuestionarioPreguntas, cuestionarioRespuestas, cuestionarioEnlacePR WHERE cuestionarioEnlacePR.idPreguntaFK = cuestionarioPreguntas.id AND cuestionarioEnlacePR.idRespuestaFK = cuestionarioRespuestas.id AND cuestionarioPreguntas.idTipoFK = " . $request->id);
  • $respuestasCorrectas: Necesitamos saber qué respuesta es la correcta para cada pregunta por lo que simplemente tenemos que coger la consulta anterior y añadirle AND cuestionarioEnlacePR.RespuestaCorrecta = 1 al final del todo. Recordemos que teniamos una columna en cuestionarioEnlacePR de tipo boolean para esto.

Si lo juntamos todo y le ponemos una validación al request para evitar sql injection nos quedaria esta funcion:

                                
class inputController extends Controller
{
    public function quiz(Request $request)
    {
        //verificar que es númerico para evitar sql inyection 
        $validatedData = $request->validate(
            ['id'=>'required|integer']
        );

        //preguntar a la base de datos por todos los posts
        $tipos =\DB::select("SELECT nombre FROM `cuestionarioTipo` WHERE `id` = " . $request->id);
        $preguntas = \DB::select("SELECT cuestionarioPreguntas.pregunta, cuestionarioPreguntas.id FROM cuestionarioPreguntas WHERE cuestionarioPreguntas.idTipoFK = ". $request->id);
        $respuestas = \DB::select("SELECT cuestionarioPreguntas.id AS 'idPregunta', cuestionarioRespuestas.id AS 'idRespuesta', cuestionarioRespuestas.respuesta FROM cuestionarioPreguntas, cuestionarioRespuestas, cuestionarioEnlacePR WHERE cuestionarioEnlacePR.idPreguntaFK = cuestionarioPreguntas.id AND cuestionarioEnlacePR.idRespuestaFK = cuestionarioRespuestas.id AND cuestionarioPreguntas.idTipoFK = " . $request->id); 
        $respuestasCorrectas = \DB::select("SELECT cuestionarioPreguntas.id AS 'idPregunta', cuestionarioRespuestas.id AS 'idRespuesta', cuestionarioRespuestas.respuesta FROM cuestionarioPreguntas, cuestionarioRespuestas, cuestionarioEnlacePR WHERE cuestionarioEnlacePR.idPreguntaFK = cuestionarioPreguntas.id AND cuestionarioEnlacePR.idRespuestaFK = cuestionarioRespuestas.id AND cuestionarioPreguntas.idTipoFK = ". $request->id ." AND cuestionarioEnlacePR.RespuestaCorrecta = 1");

        return view ("quiz", compact("tipos", "preguntas", "respuestas", "respuestasCorrectas"));
    }

}
                                
                            

Cuestionario

Despues de una de las cosas más complicadas del proyecto viene lo más simple. Vamos a crear una estructura html para que sirva de contenedor a nuestras pregutnas generadas con JavaScript.

Para las pregutnas como tal tendremos cuatro div's los cuales son "preguntas", "respuesta", "mensaje" y "boton". las dos primeras creo que no hace falta que se expliquen. El apartado mensaje no dirá si hemos acertado o fallado la pregunta y el botón será para pasar a la siguiente pregunta.

Estructura visual del cuestionario

Crearemos en la misma página una segunda vista para cuando se termine el cuestionario muestre la cantidad de aciertos y fallos que hemos tenido y yo como opcional tambien he puesto unos botones de nevegación.

Estructura visual del cuestionario finalizado

JavaScript

El archivo JavaScript está guardado en la carpeta JS como quiz.js en el cual está todo lo que necesitamos para poder hacer este proyecto. Solo están afuera de este archivo la creación de las variables que tienen los datos de la base de datos que están directamente en el archivo cuestionario.html si te has descargado de github el archivo o del archivo quiz.blade.php si has creado back-end.

En este apartado no vamos a contar lo que hacen todos los métodos ya que cada uno tiene su comentario con que hace sino que explicaremos el funcionamiento que tiene.

Una vez se ha ejecutado todas las sentencias SQL de la funcion quiz y llama al archivo quiz.blade.php guardamos los datos de la consulta en variables JavaScript de la siguiente forma: var preguntasObjeto = @json($preguntas);, hacemos lo mismo con las respuestas y con las respuestas correctas. Si recordais hacíamos 4 consultas y solo hemos guardado 3, eso es porque la primera consulta, la que nos proporcionaba el nombre del cuestionario, la ponemos directamente en el título por lo que no es necesario guardar su información.

Para empezar con el archivo quiz.js tenemos tres variables para saber en qué punto del cuestionario estamos y cuantos aciertos y fallos tenemos. Si seguimos con la ejecución veremos un condicional que nos mostrará la primera pregunta y ocultará la sección que usamos cuando el cuestionario ha terminado. La forma de funcionar del cuestionario es simplemente sustituir los valores de los divs por los que nosotros queramos en cada momento lo cual lo conseguimos mediante un bucle y llevando la cuenta de las preguntas.

Para saber que respuestas tiene cada pregunta y cual es la correcta simplemente tenemos que comparar el idPregunta de cada posicion del array objeto con el id del objeto pregunta. Con lo que conseguiremos saber si la respuesta está relacionada con esa respuesta.

Cuando creamos los botones para las respuestas estos deben de tener ciertos atributos para que funcione. Primero debe tener en el id el idRespuesta, un método "comprobarRespuesta" el cual le pasamos dos valores, el primero es el id de la respuesta correcta y el segundo el id de la respuesta actuar, por último y más importante tenemos que monstrar el texto de la respuesta en el botón para que el usuario sepa cual escoger.

Al clicar una de las respuesta ejecutamos el método "comprobarRespuesta" que comparará los id pasados para ver si coinciden. Cambiará el color de los botones conforme sea necesario para decir cual es la respuesta correcta y nos montrará un mensaje de acierto o error y un botón para la siguiente pregunta. Cuando llegamos a la última pregunta el botón de siguiente nos pasará a la pantalla final en la que se visualiza los resultados.

Termiando con esto hay un método "ajustarAltura(id)" que es simplemente para ajustar la altura de la estructura gráfica a la ventana del navegador o del móvil ya que hay ciertas preguntas que no ocupan toda la pantalla y el pie de página queda descuadrado.

Conclusión

Un proyecto que será demasiado fácil a una persoan con cierta experiencia pero que a mi me ha servido para aprencer ciertos fundamentos de JavaScript y su mecánica de trabajo. He intentado que los métodos sean lo mas modular posible para que pueda ser reutilizable en otros proyectos pero seguro que se puede mejorar bastante más.

El aspecto que quizás menos me convece del proyecto ha sido la formá de estructurar el código. Al ser algo pequeño decidí hacerlo todo en el mismo documento pero no sé hasta qué punto es lo adecuado.