El MVC (Modelo-Vista-Controlador) es un patrón para construir aplicaciones web. El propósito es separar las diferentes capas del sistema independizando el desarrollo, testing y mantenimiento. El Modelo es solo la representación de datos.
La mayoría de las aplicaciones web con bases de datos agregan allí la persistencia.
La Vista es el responsable de confeccionar la presentación de nuestra aplicación al usuario, como podría ser, por poner un ejemplo, un formulario HTML. Esto también podría ser HTML, XML, JSON o imágenes. La vista podría consumirse desde un teléfono, una computadora o podría ser un XML para consumirlo desde un API.
El controlador responde a eventos (por lo general de acciones del usuario desde la vista) o procesos que, podrían ser invocados por cambios en el modelo.
HTTP Routing
El “ruteo” de la aplicación se configura usando un archivo con las rutas que se encuentra en el directorio “conf” de tu aplicación.
Propósito
El objetivo del archivo “conf/routes” es permitirle a play decidir qué controlador será el encargado de atender las solicitudes o requerimientos (HTTP request) que recibe.
Sintaxis
El archivo contiene 3 elementos principales. El primero es el método HTTP (puede ser GET o POST), el segundo elemento es el URI (como /register) y el último elemento es la clase java controlador y el nombre del método (como Application.index).
El último elemento es opcional si indicamos argumentos estáticos (static arguments) y/o especificamos el tipo de contenido (content type).
También es posible agregar comentarios usando hash (#). Un ejemplo:
# Home
GET / Forums.index
# Registration/Authentication
GET /signup Application.signup
POST /signup Application.register
GET /login Application.login
POST /login Application.authenticate
GET /logout Application.logout
GET /signup/{uuid} Aplication.confirmRegistration
GET /confirmation/{uuid} Application.resendConfirmation
# Forums
POST /forums Forums.create
GET /forums/{<[0-9]+>forumId} Forums.show
POST /forums/{<[0-9]+>forumId}/delete Forums.delete
# /public
GET /public staticDir:public
# Catch all
* /{controller} {controller}.index
* /{controller}/{action} {controller}.{action}
Este es un ejemplo bastante completo en que se pueden ver las 3 partes del archivo de configuración, los diferentes métodos HTTP usados, las rutas con elementos dinámicos incluidos y expresiones regulares (en la dirección de {<[0-9]+>forumId}) y también los controladores.
Métodos
Play soporta la especificación HTTP. Si desear que tu ruta permita cualquier método puedes usar *. Los valores válidos para usar en la primer parte son:
– GET
– POST
– PUT
– DELETE
– HEAD
– *
Si quieres tener disponible varios métodos para una URI en particular puedes hacerlo. Esto permite tener un GET y un POST en una misma URI y play responda llamando a distintas acciones según el método HTTP de la solicitud.
Route URI
El route URI es la segunda parte del proceso, poniendo a la par la solicitud HTTP y el controlador. La primer parte de los métodos HTTP y la segunda del patrón URI. Si la solicitud HTTP (HTTP request) encuentra una entrada de URI, el controlador correspondiente será invocado.
Ejemplos:
#ejemplo sencillo
GET /signup Application.signup
#dirección absoluta exacta
GET /signup/{uuid} Application.confirmRegistration
#dirección mapeada dinámicamente
GET /signup/{<[0-9]+>forumId} Forums.show
En este caso la expresión regular aceptará únicamente números y mapea los números recibidos en la URI como el parámetro forumId que será recibido por el método show en la clase Forum (clase que es de tipo controlador obviamente).
Esto hace posible tener varios elementos dinámicos en la URI. Consideremos una aplicación de fotos que organiza las fotografías en albums. La URI puede ser usada para mostrar fotografías desde un álbum en particular. Quizás pueda ser similar a lo siguiente:
GET /album/{albumId}/photo/{photoId} Photo.show
El método show de la clase Photo debería ser invocado pasando el albumId y photoId como parámetros del método. Nosotros podríamos incluir una expresión regular para ambos elementos (albumId y photoId) para asegurar que solo puedan tomar valores numéricos.
Como puedes ver, el archivo de ruteo, rutas, routing o como quieras llamarlo, puede llegar a ser un poco sofisticado aunque no deja de ser sencillo para el desarrollo. Solo debemos recordar que una URI estará compuesta por claseControlador/nombreDelMétodo
NOTA: para play los “métodos de acción” (action methods) son métodos públicos, estáticos, que retornan void, pertenencientes a una clase que extienda Controllers. Estos son requisitos necesarios para que el “ruteador” pueda vincular el pedido HTTP con la acción del controlador.
Argumentos Estáticos
Una característica opcional del archivo de rutas es la posibilidad de agregar argumentos estáticos para los métodos que apuntamos en un controlador, lo cual nos permite reutilizar código simplificando el archivo de rutas.
Por ejemplo el usado recién:
GET /album/{albumId}/photo/{photoId} Photo.show
Si buscamos que por defecto el álbum empiece a mostrar con la primer imagen, podemos reutilizar el mismo controlador indicando así:
GET /album/{albumId} Photo.show(photoId:1)
Este método recibe dos parámetros: uno es estático (el photoId que colocamos nosotros por defecto) y el otro es el que se tomará dinámicamente de la URI.
Content Types
Por efecto, cuando Play recibe una solicitud chequea el header HTML y responde con el mismo formato del archivo que lo solicitó; si la solicitud llega de un html responde html.
Si estamos buscando RSS con los mismos datos del html solo debemos especificar el formato devuelto:
GET /recent/rss News.recentStories(<span style="color: #008000;">format:’rss’</span>)
Prioridad en las Rutas (Route Priority)
El orden de prioridad es de arriba hacia abajo. Cuando una solicitud es recibida, éste lee el archivo de rutas y la primera encontrada invoca al controlador.
Contenido Estático
Para procesar rápido las consultas y facilitar el mantenimiento de la aplicación, el contenido estático se mantiene en un mismo lugar, separado del contenido dinámico que varía entre consultas.
El contenido es localizado en un directorio público, como las imágenes, js y css.
GET /public staticDic:public
CONTROLADOR
El Controlador es una de las partes centrales de una aplicación Play como parte del modelo MVC. Todos los controladores de una aplicación se encuentran en app/controllers del directorio de tu aplicación.
Propósito
El propósito es unir el modelo de dominio y los eventos que nos llegan como peticiones HTTP. El controlador no se encarga de la lógica de negocios. Esto está a cargo del modelo. En cambio el controlador recibirá el evento, leerá los parámetros que le llegarán junto con el evento y ejecuta las acciones necesarias sobre el modelo. Una vez completado esto, el controlador retornará la información correspondiente a la vista para que despliegue los resultados a la aplicación que generó el evento, que por lo general es el browser del usuario.
Si lo comparamos con otros frameworks, un controlador podría ser pensado como los HTTP Servlets de J2EE, o en Struts podría ser equivalente a un Action object.
Creando un controlador
Un controlador es una clase Java que extiende de play.mvc.Controller, y que contiene una cantidad de métodos que representan acciones. Cada acción es la vía que tiene el controlador para responder a los eventos generados por pedidos HTTP y tomar las acciones correspondientes.
NOTA: La acción en una clase controlador debe ser public, static y retornar void.
Cuando creas una nueva aplicación en Play, por defecto tendrás un controller llamado Application.java. Contiene un método simple (una action), la cual tan solo renderiza la índex page de la aplicación. La habrás visto en la aplicación que play crea por defecto.
Quizá no haya mucho que destacar salvo que la clase de tipo controlador extiende de play.mvc.Controller. También, las acciones (acedidas según el archivo de rutas) deben ser declaradas public y static. Si no lo haces así, las peticiones de HTTP (HTTP request) no podrán set “ruteadas” correctamente hacia las acciones.
Parámetros
Usualmente, nuestros controladores percisarán parámetros a ser pasados por el usuario para cumplir su cometido..
Básicamente hay tres formas de hacer esto:
- como parte de una consulta en un string, como con /article?id=123
- como parte de un body request (si un formulario lo envía usando POST)
- como parte de la URI, como con /article/123
Por ahora solo importa destacar que como se muestra en el último caso, Play ya trae resuelto esto mapeando los objetos y haciendo las llamadas con esos parámetros.
Hay dos vías que puedes utilizar cuando quieras trabajar con mapeo de parámetros.
La primera vía para usar los datos es para trabajar directamente con la variable params. La variable es definida en la super-clase Controller, así que tu podrás tener acceso directo a ella. Para acceder al parámetro, solo necesitas llamar al método get del params Map object, y pasar el nomrbe del parámetro con el que deseas trabajar. Esto es similar a hacerlo con HTTPRequest con Servlets.
String id = params.get(“id”);
y ahora viene la frase… chan chan chan: Play as always does a little bit more. 😀 ja, toma!
Con las solicitudes (request), Play puede convertir automáticamente los datos en un tipo de objeto adecuado. Todos los datos pasados vía
HTTP son texto. En nuestro ejemplo un Id de artículo pasa por ahí, es un numérico. Así que podemos pedir que Play convierta el parámetro automáticamente a un Long agregando el tipo al método get.
Long id = params.get(“id”, Long.class);
Si se agrega un parámetro al método de la acción, siempre y cuando sea el mismo nombre que el parámetro HTTP, Play automáticamente rellenará los datos a su correspondiente parámetro de Java.
Por ejemplo, si el browser solicita la pagina /article?id=123 y el archivo de rutas tiene el método show(). Para levantar el id, nosotros solamente necesitamos el parámetro llamado id del método en cuestión.
public static void show(Long id){
render(Article.findById(id));
}
Aquí, el valor HTTP del parámetro id debería automáticamente castearse a Long para llenar el id del paramámetro Java.
HTTP to Java Binding
Play maneja el mapeo de todo slos tipos primitivos de java y sus equivalente en objetos (como Long, Boolean, Float, etc). Como siempre también maneja algunos objetos más, incluyendo
-Fechas
-Archivos
-Arrays
-Custom objects (POJO)
-JPA
Fechas
//Acá este terrible problema, a veces cansa no? el manejo del formato fecha y los nulos es lo peor que te puede pasar como programador… bueno, tenía que llorar un rato.
Tenemos que seguir alguno de la lista de patrones para que a Play le alcance su inteligencia y pueda mapear las fecha correctamente correctamente
- yyyy-MM-dd’T’hh:mm:ss’Z’ // ISO8601 + timezone
- yyyy-MM-dd’T’hh:mm:ss” // ISO8601
- yyyy-MM-dd
- yyyyMMdd’T’hhmmss
- yyyyMMddhhmmss - dd’/‘MM’/’yyyy
- dd-MM-yyyy
- ddMMyyyy
- MMddyy
- MM-dd-yy
- MM’/‘dd’/’yy
Archivos
Es muy Simple. Dese la vista, una solicitud viene de varias partes/formularios como normalmente se usa para subir archivos en una aplicación web. Como siempre, una vez que el archivo llega al lado del servidor, esto puede ser mapeado directamente a un objeto Archivo.
Un ejemplo:
public static void upload(File photo) {
// manipulate the uploaded File
// …
// save the photo to the DB get an ID
// …
show(id);
}
Cuando subimos un archivo Play lo guardará en un directorio temporario. Al final del request Play borrará el archivo. Por lo tanto si quieres concervar el archivo vas a necesitar guardarlo en algún lugar.
Arrays
Los arrays pueden pasarse como parámetros HTTP usando sintaxis como la siguiente:
/article?id{0]=123&id[1]=456&id[2]=789
Esto es perfectamente válido en HTTP, y puede ser muy práctico. Por ejemplo podemos usarlo para mostrar multiples artículos en una sola página. Para recuperar los Ids sobre un array, podemos usar los estándares de notación de array o cualquiera de las colecciones de Java de los que disponemos para usar sobre las acciones de nuestros métodos.
public static void show(Long[] ids){
…
}
Ó, una colección:
public static void show(List<Long> ids){
…
}
Custom Objects (POJO)
Es posible usar parámetros HTTP y los viejos objetos Java (POJO). Mediante el uso de notación de objectname.variablename, nos permite pasar una gran cantidad de datos a la acción, mientras que lo mantenemos en sintonía con simplicidad del método.
For ejemplo, si a nuestro site suscribimos a un usuario nuevo, podríamos enviarlo en una solicitud en el objeto, como:
addSuscriber?user.name=Walter&user.login=wfranck&user.password=secreta
La sintaxis en la acción del método debe ser como:
public static void addSuscriber(User user){
…
}
También es posible tener objetos dentro de objetos. Agregando algunos puntos a la notación. Por ejemplo:
addSuscriber?user.name=Walter&
user.login=wfranck&
user.password=secreta&
user.address.street=Alguna+Calle&
user.address.postcode=B1663IHH&
user.address.country=UK
En el ejemplo anterior, el objeto User también contiene un objeto Address llamado address (recuerda que hacemos el binding via nombre de la variable) y la dirección del objeto tiene al menos 3 variables llamadas street, postcode y país.
Si allí hay otras variariables que no pasadan parámetro, estas se dejan como valores por defecto.
JPA
El API estándar de persistencia en Java, en pocas palabras es el camino simple para guardar datos en bases de datos con el modelo orientado a objetos.
El paquete de JPA es muy similar a lo que todos conocmos. La diferencia con JPA radica en si facilitamos un id en como parámetro HTTP, Play se encarga en traer el objeto JPA desde la base de datos, y luego setear los parámetros restantes desde los parámetros HTTP. Por lo tanto puedes guardar el objeto con los cambios realizados llamando con save() en el objeto JPA, sin tener que realizar alguna lógica más para cargar y actualizar el objeto antes de guardar en la base de datos.
@wfranck