Memoria de proyecto

Índice

Introducción

Is This Game Fun? es una plataforma de consulta y participación donde los usuarios pueden conseguir una respuesta clara a una pregunta frecuente cuando pensamos en adquirir o no un producto, en este caso un videojuego: ¿Merece la pena? ¿Es divertido?

Existe un gran abanico de opciones cuando queremos informaciones detalladas y análisis sobre videojuegos, pero ninguna ofrece un respuesta tan clara y simple como un monosílabo: “Si” o “No” son las únicas respuestas posibles que ofrecemos a esta pregunta.

Para ello no solo ofrecemos respuestas, sino la posibilidad de participar de ellas. La opinión personal positiva o negativa de cada usuario determina la respuesta final en forma de voto. La opción más votada se convierte al instante en la respuesta.

En este documento se explica el planteamiento, desarrollo, obstáculos y soluciones propuestas a la hora de desarrollar este proyecto.

Este documento se encuentra replicado en la siguiente dirección: http://rodribus.github.io/IsThisGameFun/
De igual manera, el repositorio de código completo se encuentra en https://github.com/RodriBus/IsThisGameFun

Nota
A lo largo del documento se encuentran anotadas como [descartado] aquellas decisiones o funcionalidades que acabaron saliendo del proyecto por motivos de plazos y tiempo.

Introduction

Is This Game Fun? is a crowd ask-n-share platform where people can get a clear answer to a common question when thinking about purchase or not a product, in this case a videogame: Is this worth it? Is this fun?

There is a full range of choices to cover detailed information reviews about videogames, but neither of this offers a simple and clear answer as a monosyllable: “Yes” or “No” are the only possible answers we offer to this question.

To fulfill this task, we offer the chance to participate in the answer. The positive or negative personal opinion of each user determines the final answer as a vote. The most voted option becomes instantly in the answer.

This document describes the approach, development, and proposed solutions to obstacles when developing this project.

This document is replicated at the following address: http://rodribus.github.io/IsThisGameFun/.
The complete code repository can be found in https://github.com/RodriBus/IsThisGameFun as well.

Note
Throughout the document are listed as [descartado] those decisions or functionalities that ended up staying out of the project because of deadlines.


La presentación de la defensa esta disponible en http://rodribus.github.io/IsThisGameFun/presentacion/index.html


Descripción del problema

Nuestra plataforma de opinión necesita diferentes funcionalidades a menudo básicas.
Se necesita desarrollar una aplicación donde almacenar cierta información a propósito de un videojuego que más tarde mostraremos al usuario.

Esta información contiene desde el nombre, las plataformas donde se lanzó o la saga o serie a la que pertenece hasta la caratula que lo identifica visualmente, entre otras.

Por otro lado el usuario debe ser capaz de contribuir a esta información con su opinión acerca del videojuego en forma de voto positivo o negativo.

Para emitir votos es necesario registrarse en la aplicación.

El balance total de votos positivos y negativos influirá de cierta manera en que posición se muestra cada juego en una suerte de escala o ranking.

Página principal

Propiedades de un videojuego

Cada juego debe tener una información básica necesaria:

Nombre
Es el título del videojuego tal como fue lanzado en Europa.
Debido a la variedad de títulos en el mercado debe permitir caracteres poco frecuentes y tener una longitud máxima bastante larga. Hemos optado por 150 caracteres que a priori deben ser suficientes para cualquier título.

Descripción
Una descripción no muy larga del videojuego. A menudo nos nutriremos de la información ofrecida por Wikipedia.

Caratula
Un archivo de imagen que represente o identifique cada videojuego con la caratula que ofrecía en su versión europea.

Plataformas disponibles
Un videojuego puede estar disponible en una o varias plataformas. (Acerca de las plataformas)

Saga
Existen videojuegos que pertenecen a una serie concreta o forman varias entregas de una misma historia. Por ello hay juegos que pueden pertenecer a una saga determinada o a ninguna.

Balance de votos
Este es el atributo clave de un juego.
El balance de votos es el total de votos positivos y negativos que ha recibido por parte de los usuarios.
Cada voto es emitido por un usuario y cada usuario solo puede votar una vez cada juego.
Este voto puede actualizarse más tarde entre positivo y negativo indistintamente.

Propiedades de una plataforma

Cada plataforma debe disponer de la siguiente información básica:

Nombre
Es el nombre oficial de la plataforma de juego.

Nombre corto
A menudo las plataformas se abrevian en siglas que las identifican a golpe de vista.

Logo [descartado]
Logo asociado a la plataforma.

Propiedades de una saga

Cada serie de videojuegos debe disponer de las siguientes propiedades:

Nombre
Nombre de la serie que la identifica entre las demás.

Descripción
Breve descripción de la serie que agrupe de forma general información y resumen de la temática general de la misma.

Logo
En general las serie tienen un logo asociado que los identifica gráficamente.

Balance de votos [descartado]
Las series pueden ser votadas al igual que los juegos. (ver juegos)

Propiedades de un usuario

Cada usuario dispone de la siguiente información:

Nombre de usuario
Es el nombre que utilizan junto a la contraseña al autenticarse en la aplicación.

Contraseña
Conjunto de caracteres que utilizan junto al nombre de usuario para autenticarse en la aplicación.

Nivel
Corresponde a su categoría en la aplicación. Existen tres niveles: baneado, usuario y administrador.

Nick
Es el nombre público del usuario que se mostrara públicamente. Puede ser diferente del nombre de usuario.

Email
Dirección de correo del usuario. Se almacena para futuros usos.

Avatar
Cada usuario puede disponer de un avatar que mostrara en su perfil.
Este avatar debe tener unas limitaciones en sus dimensiones de un máximo de 200 píxeles de alto y de ancho.

Fecha de creación y de modificación
Se almacenaran las fechas en las que el usuario se registró y cuando se modificó por última vez su perfil con fines estadísticos.

Funcionalidades disponibles para el usuario

Los usuarios deben ser capaces de:

Funcionalidades disponibles para el administrador

Los administradores disponen de todas las funcionalidades de un usuario y además:

Planificación

La planificación se ha llevado a cabo teniendo en cuenta el total de horas presupuestadas para terminar el proyecto: 30 horas por miembro con un total de 90 horas.

Definición de funcionalidades

Durante la primera etapa del proyecto se han definido entre todos los miembros que características y funcionalidades tendrá disponibles la aplicación.
Como resultado fueron definidas todas las características posibles y establecidas unas características mínimas, de tal forma que añadir las restantes o unas nuevas funcionalidades fuese solo cuestión de más tiempo.

Desarrollo

Para desarrollar la aplicación hemos optado por dividir el desarrollo en tres ámbitos a repartir entre cada miembro del grupo:

Desarrollo en servidor
Definición del modelo de datos relacional, configurar el servidor web y programar las funcionalidades ejecutadas en servidor.

Desarrollo en cliente
Definir y programar todas las funcionalidades disponibles en el ámbito del navegador cliente.

Diseño e interfaz
Definir el aspecto visual de la aplicación: ubicación de los elementos, colores, estructura del documento HTML.

Diagramas de tiempo

Finalizado el proyecto podemos comparar la gráfica de planificación definida inicialmente y la real.

Definida
Realizamos un diagrama de tiempo a modo de ejemplo visual del tiempo presupuestado para cada parte del proyecto.

Diagrama de tiempo presupuestado

Real
A lo largo del proyecto hemos ido apuntando el tiempo real que hemos necesitado para llevar a cabo el proyecto: Una suma total de 140 horas a repartir entre los tres miembros en una proporción de 3:2:2.

Diagrama de tiempo final

Arquitectura

La arquitectura de la aplicación está orientada al patrón Modelo-Vista-Controlador2.

Este patrón separa las piezas que conforman la aplicación por asuntos o ‘concerns’ de tal manera que lo que es asunto de una pieza de la aplicación no sea de la incumbencia de las demás piezas.
De esta manera se declaran las tres piezas fundamentales:

Modelo
Representa la información persistente de la aplicación. Puede ser un archivo de texto, una hoja de cálculo o, más frecuentemente, una base de datos.
La forma en la que se obtienen los datos es independiente de cómo se procesan (control) y se representan (vista).

Control
Se encarga de manipular el modelo para realizar cambios o conseguir datos para mostrarlos a través de la vista.
Es la parte interactiva de la aplicación donde se decide el curso de acción.
El controlador no conoce el origen de los datos ni como serán representados.

Vista
Representa visualmente la información conseguida por el controlador a través del modelo. No decide la información que representa ni sabe de dónde procede.

Siguiendo esta arquitectura el flujo de acción es el siguiente:

Created with Raphaël 2.1.2SECUENCIA MVCRouterRouterControllerControllerModelModelViewViewPetición HTTPInstanciaDame los datosCogiendo datos...Toma los datosGenera el HTMLcon estos datosGenerando HTML...Toma el HTMLDevuélvele esteHTML al clienteRespuesta HTTP

El cliente realiza una petición al controlador.

El controlador decide cómo debe manipular el modelo para obtener la información que solicita.

El modelo realiza las interacciones necesarias con la información para devolver la información que el controlador solicita.

El controlador envía a la vista la información que debe representar.

La vista devuelve al controlador la información que debe devolver al cliente.

El controlador devuelve la representación al cliente.

Entidades

Relación de entidades

Utilizando lo definido anteriormente en el apartado de funcionalidades sacamos las siguientes relaciones en conclusión:

Definición de entidades

Para definir como interactuara el modelo con los datos persistentes de la aplicación optamos por una base de datos relacional en MySQL.

En base a las relaciones definidas, podemos dibujar el diagrama de entidad-relacion que nos ayudará a definir la base de datos:

Diagrama de entidad-relación

Utilizando este diagrama es fácil definir la base de datos con las siguientes tablas y atributos:

User

Game

Platform

Saga

Diagrama

Finalmente podemos representar las tablas de la base de datos como un diagrama completo:

Diagrama de base de datos

Estructura de la aplicación

Introducción

Debido al limitado tiempo del que disponen los miembros del grupo de desarrollo no ha sido posible utilizar ningún framework complejo para la arquitectura que soportase MVC como se esperaba, pero gracias a las experiencias adquiridas durante el desarrollo se ha podido crear una estructura simple pero funcional inspirada en la citada arquitectura para el desarrollo de la aplicación.

Esquema de enrutamiento

Las peticiones a servidor se manejan de la siguiente forma:

El cliente pide un recurso al servidor mediante una ruta que no existe:

http://isthisgamefun.com/games/top/page/2

Mediante un módulo del servidor Apache llamado rewrite module se traduce una petición como la anterior a:

http://isthisgamefun.com/index.php?section=game&action=top&args=page/2

Utilizando este módulo convertimos una ruta en una petición con cadena de consulta o query string que será interpretado por el código PHP que contiene index.php

Fragmento de regla del modulo rewrite

RewriteRule ^(\w+)/(\w+)/((\w+/?)+)/?$ index.php?section=$1&action=$2&args=$3 [L]

Esta nueva petición hace fácil recuperar la petición en variables accediendo a $_GET

$_GET["section"] //string - P.E. "games"
$_GET["action"] //string - P.E. "top"
$_GET["args"] //string - P.E. "page/2"

La peculiaridad de $_GET["args"] es que su valor sera troceado en partes e insertado en un array utilizando / como separador.

$args = explode("/", $_GET['args']) //array P.E. ["page", "2"]

A continuación index.php hace uso de una clase llamada Router. Esta clase recibe como argumentos los parámetros de la cadena de consulta de la petición ("games", "top" y ["page", "2"]) y los utiliza para instanciar otras clases, usar sus métodos y agregarle argumentos.

Por ejemplo, en la petición anterior se estaría instanciando un objeto de tipo Games y llamando al método top() utilizando como argumento el array ["page", "2"].

$router = new Router("games", "top", ["page", "2"]);

Estas peticiones, por supuesto, tienen valores por defecto en caso de no especificarse:

Por ejemplo, en la petición:

http://isthisgamefun/admin/games/edit/19

Se instancia la clase Admin, llamando al método games() que recibe como argumento un array que contiene ‘edit’ y ‘19’.

$args = ["edit","19"];
$controller = new Admin();
$controller->games($args);

Secuencia UML

Created with Raphaël 2.1.2Peticion HTTPTransformar rutaen variables¿Existe el controlador?Instanciar la clase controladora¿Existe el metodo?Llamada al metodocon argumentosDevolver HTMLgeneradoRespuesta HTTPEstablecer metodo a "Index"Establecer controlador a "Main"yesnoyesno

Seguridad

Esta lógica plantea un problema de seguridad: ¿Y si hacen peticiones a clases con métodos sensibles o que no deberían poder utilizarse?

Poniendo por caso la clase Admin al instanciarse se realizan ciertas comprobaciones.

En este caso ¿Tiene el usuario que hace la petición permisos de administración?.
De no ser así se realiza una redirección mediante cabeceras HTTP a otro recurso y se termina la ejecución.

En cuanto a los métodos, con el simple hecho de cambiar su accesibilidad utilizando private la ejecución se hace inalcanzable para el usuario debido a que al invocarlos se produce una excepción.

Configuración

La aplicación se ejecuta utilizando ciertas variables durante la ejecución.
Estas variables contienen datos que varían en función de dónde se ejecute la aplicación, el nombre de base de datos…

Estos datos se declaran como parte de un array indexado en el archivo config.php

//Dirección del servidor MySQL
$config['db_server'] = 'localhost';
...
//Ruta del servidor apache donde se encuentra la aplicación
$config['server_root'] = '/IsThisGameFun/';

De esta manera superamos el problema de desplegar la aplicación en una máquina local, un servidor en una máquina virtual o el equipo donde se expondrá finalmente.

Construcción del script

Para que todos los recursos necesarios estén disponibles sin necesidad de incluirlos explícitamente en el script principal, se recorren los directorios clave (ver estructura) y se incluyen dinámicamente sus contenidos.

Ejemplo de inclusión de todas las funciones

//Include all the functions
foreach (glob("./functions/*.php") as $filename)
{
    include_once $filename;
}

Así la aplicación incluirá el archivo de configuración, todas las clases “Controladores”, los scripts que contienen funciones de ayuda y las clases “Entidades” que representan los datos del modelo, así como las librerías añadidas y sus dependencias.

Ejecución

Siguiendo el proceso descrito anteriormente, vamos a seguir la ejecución de principio a fin para una petición a:

http://isthisgamefun.com/main/index

Nota
Esta dirección es sinónimo de esta:
http://isthisgamefun.com/

Tras despiezar la petición HTTP se instancia el Router.

index.php

//Preparacion del script
...

//Ejecucion del controlador a través del Router
$router = new Router($section, $action, $args);

//Devolver el HTML al cliente
echo $router->start();

router.php

class Router {

    private $controller;
    private $action;
    private $args;

    public function __construct(
    $controller = "main", 
    $action = "index", 
    $args = array()) {
        $this->controller = $controller;
        $this->action = $action;
        $this->args = $args;
    }

    public function start() {
        //Comprobar que la clase existe
        if (class_exists($this->controller)) {
            //Instanciar la clase dinámicamente
            //P.E. $c = new Main();
            $c = new $this->controller();
            //Comprobar que la clase tiene el método
            if (method_exists($c, $this->action)) {
                //Invocar el método dinámicamente
                //P.E. $c->index($this->args);
                return $c->{$this->action}($this->args);
            } else {
                //Gestionar petición erronea
                ...
            }
        } else {
            //Gestionar petición erronea
            ...
        }
    }
}

A continuación se muestra un fragmento del código que define la página principal del sitio.

main.php

class main extends Controller {

    public function index($args = array()) {
        //Petición de datos al modelo
        ...
        //Utilizar los datos
        //para rellenar una plantilla
        ...
        //Devolver el HTML
        return $this->build();
    }

Todas las clases de tipo controlador heredan de la clase Controller

controller.php

class Controller {

    public $top;
    public $menu;
    public $body;
    public $bottom;
    ...

    public function __construct(
    $top = "templates/frame/top.html", 
    $menu = getScriptOutput('templates/frame/create-menu.php'), 
    $bottom = "templates/frame/bottom.html") {
    ...
    }

    public function build() {
        //Concatena todas las partes 
        //(top . menu . body . bottom)
        ...
        //Devuelve el HTML contruido
        return $html;
    }

De esta manera la ejecución sigue un camino de ida y vuelta para generar la respuesta HTML que se entrega al cliente.

Diseño de interfaz

Bootstrap

Para el diseño en cliente se ha utilizado un framework llamado Bootstrap, debido a la facilidad y rapidez a la hora de estructurar la pagina.

Tambien un añadido de este framework es el diseño que presenta, que es sin duda la tendencia que prima en la web actual.

Se ha convertido en un estardad debido a la frecuencia con la que se usa, tanto es asi que los usuarios se han acostumbrado a que la web debe ser asi.

Como extra bootstrap porporciona una configuracion por defecto que satisface muchas necesidades de usabilidad.

Colores

Hemos elegido este diseño debido a que los colores oscuros son mas agresivos y concuerdan mas con el diseño de una pagina de videojuegos.

Se han utilizado colores mate, un diseño liso sin relieves , ni escalones , en general simple, conciso y facil de usar .

Tenemos 3 colores corporativos y dos secundarios con los que se dan pequeños toques de gracia a la pagina.

Secciones

Página principal

No tenemos logo por que nuestra imagen corporativa es nuestro nombre ISTHISGAMEFUN?, por eso es necesario tener un banner significativo.

Hemos optado por un menú horizontal por la escasez de secciones de nuestra pagina.

No tenemos ninguna barra lateral en las secciones de usuarios comunes debido a que no queremos anuncios que puedan ensuciar nuestro diseño ni profundidad suficiente como para necesitar un menú lateral en ellas.

El footer por el que hemos optado es bastante reducido puesto que nuestra pagina apenas lo necesita, ya que no tenemos demasiado enlaces.

Panel de administración

En cambio en el panel de administración si hay cierta profundidad que necesita de un menú lateral, por ello hemos optado por uno en la parte izquierda, ya que creemos que sus secciones son importantes y necesita que centremos la atención del usuario de administración en ellas.

El contenido de estas secciones Consta de una lista en la que cada fila contiene la información relevante de juegos, sagas, usuarios y plataformas.

Estas secciones están dedicadas a hacer acciones CRUD.

Cada fila tiene dos botones: Uno de editar en azul y el de eliminar en rojo.

Tienen estos colores por que ademas de ser los corporativos, en toda la pagina usamos el azul como color positivo y el rojo como negativo.

Además en la parte superior de cada lista hay un boton de ADD que llama a un formulario de insercion de datos. Este boton tiene un hover en verde para asociarlo con la creacion.

Perfil de Juego

Contiene todos los datos que creemos necesarios para que el usuario se informe acerca del juego y pueda votar.

Estan estructurados de una manera amplia , visual y sencilla.

Perfil de Usuario

Debajo del avatar del usuario se ha dejado bastante espacio por que teníamos funcionalidades que no llegamos a implementar y ese espacio se reservo para cuando se implementasen.

Hay un histórico con información sobre las votaciones del usuario que consta de filas en las que el voto se representa de forma gráfica, con un icono identificativo de positivo o negativo con el color asociado, el nombre del juego en un enlace y la fecha correspondiente al voto.

Pastillas de juegos:

Pastilla General

Las pastillas se han diseñado para encapsular su el contenido dejando evidente que la información que contienen es independiente entre cada pastilla.
A parte de tener un glyhpicon en el panel de saga en forma de triangulo apuntando hacia abajo, se le ha puesto un hover para evidenciar que existe interactividad.

El balance de votos esta representado para que de un golpe de vista se obtenga toda la informacion sin necesidad de analizarla.

Al votar se oscurece la pastilla para centrar la atención del usuario en la acción que se lleva a cabo, y ademas impide mas interacciones con esa misma pastilla hasta que la accion se complete. Ademas el botón que representa el voto emitido por el usuario se queda deshabilitado, y con un color mas lavado.

En referente al desplegable de sagas anteriormente citado, el panel desplegable renueva el espacio de la pastilla para ofrecer otra información relativa sin ocupar mas espacio.

Como en un principio se iba a incluir una biblioteca de sagas igual que de juegos y no hubo tiempo, hemos optado por añadir esa información a la pastilla de cada juego.

También se incluye información relativa a las plataformas a las q pertenecen en forma de labels con abreviatura del nombre.

Como la parte mas identificativa del juego es la caratula necesita verse mucho y para ello tiene una imagen que ocupa prácticamente el 80% de la misma

Pagina Principal

Se ha usado una versión reducida de la pastilla original con una información muy concisa pero a su vez muy visual, ya que hemos incorporado una animación al hover de la imagen para que incite el clicar sobre ella e ir al perfil de juego para mas información.

MODALES

Los modales en la pagina se utilizan para rellenar o editar formularios CRUD y utilizamos modales para que no alteren el diseño cada vez que se hace interacción aprovechando que el framework ofrece esta funcionalidad.

Funcionalidades en Cliente

Para el desarrollo en entorno cliente se ha utilizado jQuery, una librería de javascript debido a que su uso facilita mucho el trabajo y es fácil de aprender y comprender.

jQuery

Para poder utilizar jQuery en tu aplicación web tienes que añadir el script que puedes descargar y añadir desde local o directamente añadir la URL que contiene el script comprimido.
En nuestro caso usamos las URL que contiene el script comprimido.

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

Desarrollo del script

Nuestra aplicación web contiene varios script que se usan para diversas funciones de la aplicación:

vote-script.js
Su función es que el usuario pueda votar juegos de forma asíncrona y así no tener que esperar a que el servidor vuelva a recargar la pagina de la aplicación.
El script envía mediante POST un JSON que contiene la id del juego al que el usuario quiere votar y el voto que ha realizado.

var data_send = JSON.stringify({game_id: id, vote: vote_value});
//Se realiza la petición al servidor.
$.post
(server_root + 'api/vote', {json: data_send},function (data) {
            var error = data.error;
            var state;
            var msg = data.message;
            ...
//Se realizan cambios según la respuesta del servidor
});

Cuando el servidor nos responde a la petición tenemos en cuenta si ha habido un error, en caso de que lo haya hay que saber cual es el error para así poder mostrar los diferentes mensajes y actualizar los datos en el entorno cliente, en el caso de que todo este bien se mostrara un mensaje de que el voto se ha realizado correctamente y se harán diferentes cambios como actualizar la barra de porcentaje que muestra la opinión total sobre el juego, cambiar los votos totales y inhabilitar el botón que has pulsado para realizar el voto

vote-profile-game.js
Este script es muy parecido al anterior, ya que sirve para poder realizar votos, pero esta vez desde el propio perfil del juego por los que se ha tenido que desarrollar otro script adaptado.
Su función es muy parecida, cuando el usuario vota se envía una petición AJAX mediante POST con el id del juego y el voto.
Según sea la respuesta del servidor se muestra un mensaje de error o de todo correcto y se realizan los cambios pertinentes como actualización de la barra de porcentaje y el numero de votos del juego.
Para la actualizacion de html en el contenido se usan funciones como estas:

function loadPanelProfile(parent) {
//Añade panel
$(parent).append('<div class="load-vote"></div>');
$('.load-vote').css({position: 'absolute', top: '0px', height: '100%', width: '90%', opacity: '0.5', background: 'black'});
}
function loadIconProfile(parent) {
//Añade el incono de loading.
$(parent).append('<img src="' + server_root + 'img/loading.gif" id=loading-icon>');
$('#loading-icon').css({position: 'absolute', top: '50%', opacity: '1', left: '40%', height: '50px'});
}
function loadMessageProfile(parent, state, msg) {
//Añade un el mensaje.
$('#loading-icon').remove();
$(parent).append('<div class="alert alert-' + state + '" >' + msg + '</div>');
$('.alert').css({position: 'absolute', top: '50%', opacity: '1', width: '90%'});
}

Estas funciones son muy parecidas en ambos script.

Se han tenido que realizar dos scripts debido a que los nombres de las clases y los contenidos no eran totalmente iguales y si editabas algún contenido podías desconfigurar el diseño del contenido de otra pagina.

load-more.js
La función de este script es que el usuario pueda cargar mas juegos al llegar al final de la pagina.

Cuando pulsas el botón load-more se realiza una petición AJAX al servidor al que le pasamos el tipo de página en la que esta el usuario, ya que si se encuentra en los juegos mas votados cargara mas juegos en el orden de votos y si se encuentra en los mas nuevos los cargará ordenados por fecha, y el numero de juegos que se van a cargar.
La petición se hace mediante el método get.

var ruta = server_root + 'api/getMoreGames/' + type + '/' + offset;
//Se realiza la petición al servidor
    $.get(ruta, function (data) {
        var parent = $('#game-container');
//Añade los juegos
        addGames(parent, data);
//Añade el metodo vote() para que puedan votar
        vote();
//Añade el método extendSaga() para que desplegarse las sagas
        extendSaga();
//Si ya no hay mas juegos inhabilita botón y cambia texto
        if(data.full_quota === false){
            $('.load-more').addClass('disabled');
            $('.load-more').text('No games left');
            }
        });

Una vez se han cargado todos los juegos el botón se inhabilita y cambia el texto por no games left y así evitar que se realicen mas peticiones.

display-saga.js
Este Script se encarga de mostrar las sagas de aquellos juegos que la tienen.
Su función es desplegar una breve descripción de la saga del juego que se seleccione, cuando se pulsa sobre “Saga” se despliega un div que contiene el nombre de la saga del juego, un logo y una breve descripcion, cuando vuelves a pulsar sobre “Saga” se cierra el div.

function extendSaga() {
$('.saga-name').click(function() {
var open $(this).find('.caret').parent().hasClass('dropup');
if (!open) {
//Si la saga no esta desplegada se despliega y cambia la flecha hacia arriba.
$(this).siblings('.saga').animate({height: '100%'}, 500);  
$(this).find('.caret').parent().addClass('dropup');
open = $(this).find('.caret').parent().hasClass('dropup');

} else {
//Si la saga esta desplegada se cierra y cambia la flecha hacia abajo.
$(this).siblings('.saga').animate({height: '0px'}, 500);          $(this).find('.caret').parent().removeClass('dropup');
open = $(this).find('.caret').parent().hasClass('dropup');
        }
    });
}

sticky-menu.js
La función de este script es que al hacer scroll sobre la pagina web que se esta mostrando no pierdas el menú de navegación y así facilite mas la navegación para el usuario.
Si el usuario hace scroll y pierde el menú de vista este se hará fijo y bajara con el scroll, en caso contrario subirá y se colocara en su sitio.

$(function () { // document ready
// returns number 
var stickyTop = $('.sticky').offset().top; 
// scroll event  
$(window).scroll(function () { 
// returns number
var windowTop = $(window).scrollTop(); 
if (stickyTop < windowTop) {
//Si la posición en pantalla del menú es menor que la de la pagina. 
//Añade css para que el menú se quede fijado y al frente.
}
else {
//Añade el css original al menú de navegación para que se quede en su sitio.
        }
    });
});

Validaciones

Para el formulario de registro de nuestra aplicación web hemos incluido validaciones en servidor y en cliente.
Cuando el usuario rellena un campo y sale de este se lanza un script que comprueba si el campo esta escrito correctamente, en caso de que este bien se muestra un ‘tic’, en caso contrario se muestra un ‘x’ y un mensaje de error.

validate-signin.js
Este script se encarga de hacer la validación campo a campo y de hacer los correspondientes cambios.
Para las validaciones hemos desarrollado diferentes funciones, reciben el campo que evalúan y en caso de que sea correcto retornan true.

//Funciones de evaluación
function validateUserName(user) {
    var regexp = /^[a-zA-ZÑñ0-9_-]{3,45}$/;
    return regexp.test(user.val());
}

function validateEmail(email) {
    var regexp = /^[a-zA-Z0-9\._-]+@[a-zA-Z0-9-]{2,}[.][a-zA-Z]{2,4}$/;
    return regexp.test(email.val());
}

function validatePass(pass) {
    var regexp = /^[a-zA-ZÑñ0-9_-]{3,45}$/;
    return regexp.test(pass.val());
}
function validateNick(nick) {
    var regexp = /^[a-zA-ZÑñ0-9_-]{3,45}$/;
    return regexp.test(nick.val());
}
function isTheSamePass(pass){
    var confirm = true;
    if ($('#confirmPassword').val()!== ''){
        if (pass.val() === $('#confirmPassword').val()){
            confirm = true;
        } else {
            confirm = false;
        }
    }
    return confirm;
}

Una vez se han rellenado bien todos los campos se habilita el botón signin.

function enableSubmit() {
    if (userIsValid && emailIsValid && passIsValid && pass2IsValid && nickIsValid) {
        $('#btn-submit').removeClass('disabled');
    } else {
        $('#btn-submit').addClass('disabled');
    }
}

Esta función se ejecuta cada vez que el usuario cambia de campo para así comprobarlo siempre.

Ejemplo de función de un campo del formulario:

    $('#user').blur(function () {
        var user = $(this);
        var isValid = validateUserName(user);
        //poner colorines, iconos y mensajes
        includeIcon(user, isValid);
        userIsValid = isValid;
        enableSubmit();
    });

La función includeIcon se encarga de añadir o cambiar el check o la ‘x’ y del mensaje que se muestra al usuario.

function includeIcon(element, isValid){
if (isValid) {
//poner tick.
         element.siblings('.glyphicon').removeClass('glyphicon-remove');         element.siblings('.glyphicon').addClass('glyphicon-ok');
element.siblings('.help-block').text("The name you will log in with.")
element.siblings('.help-block').removeClass('error');

} else {
//poner x y sacar mensaje de error
            element.siblings('.glyphicon').removeClass('glyphicon-ok');          element.siblings('.glyphicon').addClass('glyphicon-remove');
element.siblings('.help-block').text("Alphabetical, numerical, - and _ characters only.")
element.siblings('.help-block').addClass('error');
    }
}

Una vez se han realizado todas las comprobaciones se envía el formulario.

$('#form-signin').submit(function () {
    return userIsValid && emailIsValid && passIsValid && pass2IsValid && nickIsValid;
});

check-usernick.js
La función de este script es comprobar que el nick que el usuario ha elegido no pertenezca a ningún usuario ya registrado.
Se realiza una petición AJAX mediante GET a la que se le envía el nick que el usuario quiere para su registro.

var data_send = JSON.stringify({user_nick: user_nick});
$.get(ruta, function (data) {
//Se realiza la petición al servidor.
    var error = data.error;
    var message = data.message;
    var exists = data.exists;
    nickIsValid = !exists;
    var state;
    (error) ? state = "remove" : ((exists) ? state = "remove" : state = "ok");
    addNickResultIcon(field, state, message);
    enableSubmit();
});

Según la respuesta del servidor se mostrara el mensaje de error y un check o una ‘x’.

Funcionalidades descartadas y mejoras a futuras

Debido al límite de tiempo y las obligaciones académicas de cada miembro ciertas funcionalidades fueron descartadas durante el desarrollo.

Funcionalidades

Voto a sagas
Al comienzo del desarrollo se pensó en extender la acción de votar también a las sagas como forma de tener una forma mas “generalista” de opinar sobre ciertos juegos.
A pesar de que acabo fuera del desarrollo, la estructura de la base de datos aun contempla su implementación.

Multiples sagas
Al principio se definió que cada juego podia pertenecer a mas de una saga. Esta funcionalidad fue descartada por motivos de diseño de la interfaz: con el tiempo limitado no fue posible integrar múltiples sagas de forma adecuada en el diseño.
A pesar de que acabo fuera del desarrollo, la estructura de la base de datos aun contempla su implementación.

Redimensión de imagenes y creación de thumbnails
Debido a la gran cantidad de imágenes que se utilizan en la aplicación se pensó redimensionar y comprimir las imágenes subidas por la administración para mejorar el rendimiento de la aplicación, pero acabo descartada tras unos pocos intentos y debido al poco tiempo de margen para implementarla.

Información de perfil de usuario
Al principio se propuso que cada usuario pudiera mostrar alguna información personal concreta, como su genero, su edad o videojuego, genero y saga preferidos… De nuevo el tiempo jugó en contra dejándolo fuera del proyecto.

Amigos
Al igual que otras, esta funcionalidad se propuso al principio del proyecto cuando se profundizaba en la parte de interacción del usuario.
A pesar de que estaba prevista no hubo tiempo de implementarla.

Propuesta de juegos
Una de las primeras ideas fue proporcionar a los usuarios una herramienta con la que proponer los siguientes juegos que serían añadidos a la base de datos.
A pesar de que nos pareció una buena idea no teníamos claro como incluirla en la aplicación sin depender finalmente de un administrador.

Mejoras a futuro

El siguiente paso para ampliar la aplicación sería implementar todas las funcionalidades que por plazo no pudieron ser incluidas.

Pero una de las características mas interesantes de la aplicación es su escalabilidad y portabilidad: siguiendo la estructura actual es fácil extender las funcionalidades según vayan apareciendo pero, sobretodo, se pueden crear otras aplicaciones con otra temática y funcionalidad similar.

Así por ejemplo con unos pocos ajustes puedes tener una aplicación de voto de libros, películas, series, cómics, o cualquier otro producto de entretenimiento.

Conclusión

El tiempo estimado para desarrollar la aplicación se calculaba entorno a las 90h en total, pero se han invertido mas de 110h a pesar del tiempo limitado del que disponían los miembros del grupo.

Hemos tenido que lidiar con la planificación, la organización, la distribución de tareas y el propio desarrollo.
Gracias a este trabajo se han podido superar obstáculos durante el desarrollo que habrían trabado en incluso supuesto el fin del proyecto de no haberse organizado adecuadamente.
Esto no habría sido posible sin la cooperación constante con la que el equipo ha desempeñado el trabajo.

La tendencia organizativa y modular del desarrollo ha permitido y permitirá introducir funcionalidades con poco esfuerzo para poder llevar la aplicación a un nivel de funcionalidad más avanzado.

Glosario de términos

  1. CRUD: Acrónimo de Crear, Obtener, Actualizar y Borrar (del original en inglés: Create, Read, Update and Delete).
  2. MVC: Modelo-Vista-Controlador, arquitectura de software que separa los datos y la lógica de negocio de una aplicación de la interfaz de usuario y el módulo encargado de gestionar los eventos y las comunicaciones.

Bibliografía

HTML

CSS

JavaScript

jQuery

Bootstrap

PHP

MySQL

Medoo - Documentación

Apache - Documentación

GitHub - Documentación

General

Apuntes, trabajos y proyectos realizados durante el curso

Anexos

Estructura de directorios

La aplicación se estructura en directorios de la siguiente forma:

.
├── assets
│   ├── styles
│   │   ├── style1.css
│   │   ├── ...
│   │   └── styleN.css
│   ├── fonts
│   │   ├── font1.otf
│   │   ├── ...
│   │   └── fontN.otf
│   ├── images
│   │   └── ...
│   └── scripts
│       ├── script1.js
│       ├── ...
│       └── scriptN.js
├── avatars
│   └── ...
├── classes
│   ├── class1.php
│   ├── ...
│   └── classN.php
├── controllers
│   ├── controller1.php
│   ├── ...
│   └── controllerN.php
├── covers
│   └── ...
├── functions
│   ├── function1.php
│   ├── ...
│   └── functionN.php
├── logos
│   └── ...
├── templates
│   ├── controller1
│   │   ├── template1.html
│   │   ├── ...
│   │   └── templateN.html
│   ├── ...
│   └── controllerN
│       ├── template1.html
│       ├── ...
│       └── templateN.html
├── vendor
│   └── ...
├── .htaccess
├── autoinclude.php
├── config.php
└── index.php

Herramientas utilizadas

Virtualización
Oracle VM VirtualBox

Servidor
Ubuntu Server 14.04

Servidor Web
Apache 2 con modulo rewrite

Gestor de Bases de Datos
MySQL

Diseño de Bases de Datos
MySQL Workbench

Servidor FTP
ProFTPD

Control de versiones
GitHub

Gestor de dependencias
Composer

Editor de texto
StackEdit

Presentaciones
Reveal.js

IDE’s
NetBeans 8 con XDebug como debugger de PHP

Desarrollo
Google Chrome ver. 43 y Mozilla Firefox ver. 38 como navegadores
FireBug 2.0 como debugger de JavaScript

Librerías utilizadas

En este proyecto hacemos uso de varias herramientas de terceros.

Kint
Una herramienta de depuración para PHP.
Se describe a sí mismo como:

var_dump() y debug_backtrace() con esteroides.

Referencia: http://raveren.github.io/kint/

Medoo
Un framework ligero de acceso a base de datos.
Simplifica la creación de cadenas de consulta y obtención de datos.
Se describe a sí mismo como:

El framework de interacción con base de datos en PHP más ligero que acelera el desarrollo

Referencia: http://medoo.in/

XDebug
Una extensión de PHP que aporta herramientas para perfilar y depurar código

Bootstrap
Un framework de diseño de sitios y aplicaciones web.
Referencia: http://getbootstrap.com/

SlickMap
Una librería de estilos simple para crear mapas de navegación web.
Referencia: https://www.astuteo.com/slickmap/

jQuery
Una librería de Javascript que permite simplificar la manera de interactuar con los documentos HTML, manipular el árbol DOM, manejar eventos, desarrollar animaciones y agregar interacción con la técnica AJAX a páginas web.
Referencia: https://jquery.com/


Escrito con StackEdit.