Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
FrontController
54
ARQUITEC TURA
55
del usuario. Desde el punto de vista del desarrollador, MVC nos permite mantener
individualizadas, las responsabilidades dentro de un sistema, permitindonos
diferenciar y aislar el diseo del sistema (objetos que componen los modelos), de la
interfaz grfica del usuario (GUI) y su correspondiente lgica de negocios (vistas),
utilizando como conector intermediario un objeto controlador.
El orden modelo-vista-controlador, es el que el programador debe seguir en el proceso
de desarrollo del sistema. Sin embargo, desde el punto de vista del usuario, todo
comienza en el controlador. Veamos cmo:
A nivel funcional, en MVC todo se inicia con una solicitud del usuario (peticin);
Dicha solicitud (algo que el usuario quiere hacer con respecto al sistema), es
representada mediante la estructura de la URI;
Cuando la solicitud del usuario es enviada, sta es recibida por el controlador del
modelo, quien se comunica con ste solicitndole la informacin necesaria;
Una vez que el modelo retorna la informacin al controlador, ste le entrega dicha
informacin a la vista del modelo, quien ser la encargada de procesar la
informacin recibida, colocarla en la GUI y mostrrsela al usuario.
Veamos un ejemplo:
# MODELO: /myapp/modulo/models/vidrio.php
class Vidrio {
function __construct() {
$this->vidrio_id = 0;
$this->color = '';
}
function save() {
# Guarda un nuevo objeto u objeto existente
}
function get() {
# Recupera un objeto
}
function destroy() {
# Destruye un objeto
}
}
2012 Hackers & Developers Magazine Creative Commons Atribucin NoComercial CompartirIgual 3.0. www.hdmagazine.org
# MODELO: /myapp/modulo/models/vidrio.py
class Vidrio(object):
def __init__(self):
self.vidrio_id = 0
self.color = ''
def save(self):
"""Guarda un nuevo objeto u objeto existente"""
pass
def get(self):
"""Recupera un objeto"""
pass
def destroy(self):
"""Destruye un objeto"""
pass
# GUI (para PHP): /myapp/static/html/ver_vidrio.html
<h1>Vidrio {vidrio_id}</h1>
<p>Vidrio de color {color}.</p>
# VISTA: /myapp/modulo/views/vidrio.php
class VidrioView {
function ver($objeto=NULL) {
settype($objeto, 'array');
$comodines = array_keys($objeto);
foreach($comodines as &$comodin) {
$comodin = "\{$comodin\}";
}
$valores = array_values($objeto);
$template = file_get_contents("/myapp/static/html/ver_vidrio.html");
print str_replace($comodines, $valores, $template);
}
}
2012 Hackers & Developers Magazine Creative Commons Atribucin NoComercial CompartirIgual 3.0. www.hdmagazine.org
56
57
# CONTROLADOR: /myapp/modulo/controllers/vidrio.php
class VidrioController {
function ver($id=0) {
$vidrio = new Vidrio();
$vidrio->vidrio_id = $id;
$vidrio->get();
$view = new VidrioView();
$view->ver($vidrio);
}
}
# CONTROLADOR: /myapp/modulo/controllers/vidrio.py
class VidrioController(object):
def ver(self, id=0):
vidrio = Vidrio()
vidrio.vidrio_id = id
vidrio.get()
view = VidrioView()
self.output = view.ver(vidrio)
Si el usuario quisiera ver el vidrio con id 15, su solicitud sera enviada a travs de:
http://mymvcapp.net/modulo/vidrio/ver/15 y la misma, sera tramitada por
VidrioController, pero cmo llegar la solicitud a VidrioController? Para responder a
esta pregunta, tendremos que hablar de FrontController.
Si no sabes como codear una Web en Python crudo (sin Frameworks) corriendo bajo Apache, te
recomiendo leer un artculo que publiqu en Debian Hackers Una Web en Python sobre Apache en 3
pasos ingresando en http://www.debianhackers.net/una-web-en-python-sobre-apache-sinframeworks-y-en-solo-3-pasos
2012 Hackers & Developers Magazine Creative Commons Atribucin NoComercial CompartirIgual 3.0. www.hdmagazine.org
cantidad = len(peticiones)
# Obtengo el mdulo, modelo, recurso (y argumentos, si existen)
if cantidad == 3:
modulo, modelo, recurso = peticiones
elif cantidad == 4:
modulo, modelo, recurso, arg = peticiones
58
59
recibir dos parmetros: el recurso (para hacer la llamada dinmica al mtodo actuante) y
el argumento (que pueda ser requerido por algunos mtodos):
class VidrioController(object):
def __init__(self, recurso='', arg=0):
getattr(self, recurso)(int(arg))
def ver(self, id=0):
vidrio = Vidrio()
vidrio.vidrio_id = id
vidrio.get()
view = VidrioView()
self.output = view.ver(vidrio)
usuario:
# Divido la URI utilizando como separador la barra diagonal
$peticiones = explode('/', $_SERVER['REQUEST_URI']);
# Cuento las peticiones, para saber si hay o no argumentos
$cantidad = count($peticiones);
# Obtengo el mdulo, modelo, recurso (y argumentos, si existen)
if($cantidad == 3) {
list($modulo, $modelo, $recurso) = $peticiones;
} elseif($cantidad == 4) {
list($modulo, $modelo, $recurso, $arg) = $peticiones;
}
Luego, con los datos obtenidos, est en condiciones de importar el archivo del
controlador , instanciarlo y entregarle la informacin necesaria para que ste acte:
# Obtengo el nombre del controlador
$controller_name = ucwords($modelo) . "Controller";
# Para poder importar el controlador, debo agregar el path de la aplicacin
ini_set('include_path', str_replace('frontcontroller.php', '',
$_SERVER['SCRIPT_FILENAME']));
# Importo el mdulo del controlador
require_once("$modulo/controllers/$modelo.php");
# Instancio al controlador y le envo el recurso y argumentos
$controller = new $controller_name($recurso, $arg);
2012 Hackers & Developers Magazine Creative Commons Atribucin NoComercial CompartirIgual 3.0. www.hdmagazine.org
60
61
74
M ANUAL DE M VC
reo que la consulta ms frecuente que he recibido desde que publiqu mi libro
POO y MVC en PHP, ha sido cmo evitar embeber cdigo de programacin en el
HTML para completar una tabla o lista de seleccin ( select) con los resultados de
una consulta a base de datos. Y cranme: es sumamente sencillo.
Primero, es necesario saber, que existen tres tipos de sustituciones que pueden
efectuarse en las vistas:
1. Sustituciones estticas: es el caso de una plantilla HTML en la que se deben
sustituir ciertos datos de forma esttica. Es decir, plantillas HTML en las que cada
indicador de sustitucin, debe ser reemplazado por un valor concreto.
2. Sustituciones dinmicas: son aquellas plantillas HTML en las cules un mismo
indicador de sustitucin, debe ser reemplazado de forma iterativa por ms de un
dato. Por ejemplo, una lista de seleccin que deba reemplazarse con los
resultados devueltos por una consulta de seleccin mltiple a base de datos.
2012 Hackers & Developers Magazine Creative Commons Atribucin NoComercial CompartirIgual 3.0. www.hdmagazine.org
75
2012 Hackers & Developers Magazine Creative Commons Atribucin NoComercial CompartirIgual 3.0. www.hdmagazine.org
76
77
<h1>$titulo</h1>
</header>
<article>
$contenido
</article>
</body>
</html>
Mientras que en PHP, no existe ninguna regla al respecto, quedando al libre albedro del
diseador, la eleccin de estilo de los identificadores de sustitucin. Un clsico en los
identificadores de sustitucin en PHP, es encerrar las palabras identificadoras entre
dos llaves, de apertura y cierre respectivamente {}:
<!doctype html>
<html lang="es">
<head>
<charset="utf-8">
<title>{titulo}</title>
</head>
<body>
<header>
<h1>{titulo}</h1>
</header>
<article>
{contenido}
</article>
</body>
</html>
78
plantilla = archivo.read()
Sustituciones estticas
Tanto en Python como en PHP, las sustituciones estticas se realizan siguiendo los tres
pasos estndar mencionados en el punto anterior: traer la plantilla, crear el diccionario y
realizar la sustitucin.
Si los datos sustitutos debieran traerse desde una consulta a base de datos, primero
se realizar la consulta SQL, luego se almacenarn los datos retornados en variables y,
finalmente, dichas variables se asignarn como valores de las claves del diccionario, sin
ms complejidad que sta.
2012 Hackers & Developers Magazine Creative Commons Atribucin NoComercial CompartirIgual 3.0. www.hdmagazine.org
79
Sustituciones dinmicas
En las sustituciones dinmicas, la mayor complejidad radica en la obtencin del cdigo
HTML sobre el cual la sustitucin debe realizarse.
La plantilla HTML se deber obtener de forma estndar, mientras que de ella, antes de
proceder con los dos pasos siguientes, se deber recuperar slo la fraccin de cdigo
HTML sobre la cul realizar los reemplazos. Dicha fraccin de cdigo, se obtendr
definiendo previamente, la siguiente expresin regular:
<!--NOMBRE-DE-LA-SUSTITUCION-DINAMICA-->(.|\n){1,}<!--NOMBRE-DE-LA-SUSTITUCION-DINAMICA-->
Una vez obtenido el match (fragmento de cdigo HTML coincidente con la expresin
regular), tanto el diccionario como las sustituciones, debern realizarse de manera
estndar pero dentro de un bucle, condicionado por la cantidad de registros obtenidos
de la consulta SQL. La nica salvedad, es que ambos pasos debern realizarse en la
misma estructura de control cclica y, el resultado de cada sustitucin, sumarse dentro de
una misma variable. La sustitucin, en este caso, se efectuar sobre el match y no sobre la
plantilla.
Sustitucin iterativa en PHP:
$render = "";
foreach($registros as $array) {
$diccionario = array(
"{nombre}"=>$array[0],
"{apellido}"=>$array[1],
"{telefono}"=>$array[2]
);
$render .= str_replace(array_keys($diccionario),
2012 Hackers & Developers Magazine Creative Commons Atribucin NoComercial CompartirIgual 3.0. www.hdmagazine.org
80
array_values($diccionario), $match);
Sustituciones combinadas
Cuando en una misma plantilla se necesite efectuar una sustitucin esttica y una o ms
sustituciones dinmicas, el proceso se har paso a paso. Esto significa, que en primer
render, deber
lugar, se efectuar la sustitucin esttica. El resultado obtenido en ese
match para la sustitucin dinmica. Es decir, que
ser aquel sobre el cul se realice el
cuando se llegue al proceso de sustitucin dinmica, no se volver a traer la plantilla,
sino que se utilizar el cdigo ya renderizado, obtenido en el proceso de sustitucin
esttica.
2012 Hackers & Developers Magazine Creative Commons Atribucin NoComercial CompartirIgual 3.0. www.hdmagazine.org
32
MANUAL DE MVC
Dichos algoritmos, formarn parte de los mtodos de cada una de las vistas, pudiendo
adems, crearse objetos View a nivel del core, para ser reutilizados en las vistas de cada
modelo.
ABM, tambin son frecuentes otros dos mtodos: uno para mostrar el listado de una
coleccin de objetos determinada y otro, para visualizar un objeto puntual. Por
supuesto, la cantidad de mtodos de una vista, depende solo y exclusivamente de los
requerimientos grficos de cada aplicacin. No obstante, el objetivo de esta entrega, es
ver como crear los objetos View. Luego, la cantidad de mtodos a desarrollar,
depender del lector y de los requerimientos de su GUI.
Los mtodos de la vista, sern invocados por el controlador del modelo. ste, ser quien
entregue los datos a la vista, mientras que la ltima, ser la encargada de realizar las
sustituciones pertinentes e imprimir el resultado de las mismas en pantalla.
Bsicamente, la secuencia podra describirse como la siguiente:
1. El usuario solicita un recurso a travs del navegador
2. El FrontController (descrito en la edicin #1), analiza la peticin del usuario e
instancia al controlador correspondiente (veremos controladores ms adelante)
3. El constructor de cada controlador, es quien realiza una llamada de retorno a un
mtodo propio el cual se encargar de:
4. La vista, traer las plantillas necesarias, para sustituir los datos que le han sido
entregados por el controlador. Finalmente, imprimir en pantalla, el resultado de
dicha sustitucin.
14 http://www.hdmagazine.org/?magazine=HackersAndDevelopers&num=2
Pg. 33
# en Python
class VidrioView(object):
pass
Suponiendo que para nuestro modelo Vidrio hayamos definido un recurso agregar (que
deber ser un mtodo del controlador, como veremos ms adelante), en principio,
debemos contar con la GUI para este recurso (aqu no incluiremos an la plantilla
general, sino solo el contenido relativo a este recurso. La plantilla general se incluir al
final de este artculo):
<h3>Agregar un nuevo vidrio</h3>
<form method="POST" action="/mimodulo/vidrio/guardar">
<!-- notar que el nombre de los campos, debe coincidir con el nombre de las
propiedades del objeto, siempre que esto sea posible-->
<label for="grosor">Grosor:</label><br/>
<input type="text" name="grosor" id="grosor" size="3"/> mm<br/><br/>
<label for="sup">Superficie:</label><br/>
<input type="text" name="superficie" id="sup"/><br/><br/>
<label for="color">Color:</label><br/>
<input type="text" name="color" id="color"/><br/><br/>
<input type="submit" value="Guardar"/>
</form>
Como podemos observar, la GUI siempre debe ser el primer paso en el proceso de
desarrollo de las vistas.
En este ejemplo en particular, nos encontramos con que la GUI, no requiere de ninguna
sustitucin (ni esttica ni dinmica). Entonces, lo nico que necesitaremos tener en la
lgica de nuestra vista, es un mtodo que traiga dicha GUI y la muestre en pantalla. Este
agregar(). Podra tener un nombre ms
mtodo, no necesariamente debe llamarse
descriptivo como por ejemplo, mostrar_form_alta():
# PHP: Archivo /myapp/modulo/views/vidrio.php
class VidrioView {
function __construct() {
}
function mostrar_form_alta() {
$plantilla = file_get_contents("/ruta/a/agregar_vidrio.html");
print $plantilla;
}
}
Pg. 34
def mostrar_form_alta(self):
with open("/ruta/a/agregar_vidrio.html", "r") as archivo:
plantilla = archivo.read()
print plantilla
En este caso, la vista deber contar con un mtodo, que se encargue de realizar una
sustitucin esttica. Para esto, el controlador, le deber pasar un objeto Vidrio como
parmetro. Ampliemos el ejemplo anterior:
# PHP: Archivo /myapp/modulo/views/vidrio.php
class VidrioView {
function __construct() {
}
function mostrar_form_alta() {
$plantilla = file_get_contents("/ruta/a/agregar_vidrio.html");
print $plantilla;
}
function mostrar_objeto($objeto_vidrio) {
# Traigo la plantilla
$plantilla = file_get_contents("/ruta/a/ver_vidrio.html");
# Creo el diccionario
settype($objeto_vidrio, 'array');
foreach($objeto_vidrio as $clave=>$valor) {
$objeto_vidrio["{{$clave}}"] = $valor;
Pg. 35
unset($objeto_vidrio[$clave]);
}
# Realizo la sustitucin
$render = str_replace(array_keys($objeto_vidrio),
array_values($objeto_vidrio), $plantilla);
# Imprimo el resultado en pantalla
print $render;
}
}
Pg. 36
A nivel del core, se podr tener una vista para la sustitucin de la plantilla general. La
misma, podr realizarse mediante la llamada esttica a un mtodo de clase o a una
funcin (fuera del contexto de una clase). Aqu, lo haremos en el contexto de una clase.
Crear una clase con un mtodo esttico, es una buena alternativa para centralizar
cualquier otro mtodo relacionado con las vistas, directamente disponible desde el
ncleo de la aplicacin.
2013 HDMagazine.org Creative Commons Atribucin NoComercial CompartirIgual 3.0 Unported
Pg. 37
# Archivo: /myapp/core/view.php
class CoreView {
public static function show($modulo, $modelo, $recurso, $render) {
$plantilla = file_get_contents("/ruta/a/template.html");
$diccionario = array(
"{APP_TITLE}" => APP_TITLE,
"{MODULE_TITLE}" => ucwords($modulo),
"{MODULO}" => $modulo,
"{MODELO}" => $modelo,
"{MODEL_TITLE}" => ucwords($modelo),
"{RESOURCE_TITLE}" => ucwords("$recurso $modelo"),
"{CONTENIDO}" => $render
);
print str_replace(array_keys($diccionario), array_values($diccionario),
$plantilla);
}
}
# Archivo: /myapp/core/view.py
class CoreView(object):
def __init__(cls):
pass
def show(cls, modulo, modelo, recurso, render) {
with open("/ruta/a/template.html", "r") as archivo:
plantilla = archivo.read()
diccionario = dict(
APP_TITLE=APP_TITLE,
MODULE_TITLE=modulo.title(),
MODULO=modulo,
MODELO=modelo,
MODEL_TITLE=modelo.title(),
RESOURCE_TITLE="%s %s" % (recurso.title(), modelo.title()),
CONTENIDO=render
)
return Template(plantilla).safe_substitute(diccionario)
Luego, solo ser necesario que cada una de las vistas, en vez de imprimir el resultado de
la sustitucin en pantalla, imprima el resultado de la llamada esttica al mtodo show()
de CoreView:
# PHP: Modificacin del archivo /myapp/modulo/views/vidrio.php
function mostrar($objeto_vidrio) {
$plantilla = file_get_contents("/ruta/a/ver_vidrio.html");
settype($objeto_vidrio, 'array');
Pg. 38
foreach($objeto_vidrio as $clave=>$valor) {
$objeto_vidrio["{{$clave}}"] = $valor;
unset($objeto_vidrio[$clave]);
}
$render = str_replace(array_keys($objeto_vidrio),
array_values($objeto_vidrio), $plantilla);
# Se imprime el resultado de la llamada esttica al mtodo show()
print CoreView::show('modulo', 'vidrio', 'ver detalles de', $render);
}
Generalmente, siempre se tendr un objeto y lo que se necesitar, ser un diccionario formado por pares de
clave-valor, donde los valores, no sean del tipo coleccin (es decir, sean de un tipo de datos simple). Convertir
un objeto en un diccionario, es la base de la lgica de las vistas en MVC.
En la prxima entrega, nos enfocaremos en los controladores de los modelos,
para ir finalizando nuestro Manual de MVC en Python y PHP.
17
M ANUAL DE MVC
class VidrioController(object):
pass
# En PHP
class VidrioController { }
FrontController le entregue un
En el caso de Python, adems, ser muy til que el
tercer parmetro: el diccionario environ para que el controlador pueda recuperar los
POST) a travs de la clave
wsgi.input (si
datos enviados desde los formularios (va
existe). En este caso, el controlador, deber disponer de dicho valor en una propiedad:
Por favor, notar que esto es solo necesario si se est trabajando en el supuesto de WSGI sobre Apache.
# En Python
class VidrioController(object):
def __init__(self, recurso='', argumento=0, environ={}):
self.pd = env['wsgi.input'].read() if 'wsgi.input' in environ else ''
Los mtodos constructores debern realizar una llamada de retorno a los recursos
correspondientes, pasndoles el argumento como parmetro:
# En Python
class VidrioController(object):
def __init__(self, recurso='', argumento=0, environ={}):
self.pd = env['wsgi.input'].read() if 'wsgi.input' in environ else ''
getattr(self, recurso)(int(argumento))
# En PHP
class VidrioController {
function __construct($recurso='', $argumento) {
call_user_func(array($this, $recurso), $argumento);
Pg. 18
}
}
Los recursos editar(), eliminar() y ver() suelen recibir la ID del objeto como
parmetro (argumento recibido desde FrontController).
Pg. 19
editar,
En los recursos editar, ver y listar, entrega los datos del objeto a la
vista;
# En Python
class VidrioController(object):
def __init__(self, recurso='', argumento=0, environ={}):
self.pd = env['wsgi.input'].read() if 'wsgi.input' in environ else ''
getattr(self, recurso)(int(argumento))
def agregar(self, *arg):
# Llama directamente a la vista
vista = VidrioView()
self.output = vista.mostrar_form_alta()
def editar(self, id=0):
# Instancia al modelo
modelo = Vidrio()
# Modifica las propiedades necesarias
modelo.vidrio_id = int(id)
# Llama al mtodo correspondiente (necesita recuperar el objeto)
modelo.get()
# Le entrega la informacin a la vista
vista = VidrioView()
self.output = vista.mostrar_form_edicion(modelo)
def guardar(self, *arg):
Pg. 20
# En PHP
class VidrioController {
function __construct($recurso='', $argumento=0) {
call_user_func(array($this, $recurso), $argumento);
}
function agregar() {
# Llama directamente a la vista
$vista = new VidrioView();
$vista->mostrar_form_alta();
}
function editar($id=0) {
# Instancia al modelo
$modelo = new Vidrio();
# Modifica las propiedades necesarias
$modelo->vidrio_id = (int)$id;
# Llama al mtodo correspondiente (necesita recuperar el objeto)
$modelo->get();
# Le entrega la informacin a la vista
$vista = new VidrioView();
$vista->mostrar_form_edicion($modelo);
}
Pg. 21
function guardar() {
# Instancia al modelo
$modelo = Vidrio()
# Modifica las propiedades necesarias
$id = isset($_POST['vidrio_id']) ? (int)$_POST['vidrio_id'] : 0;
$modelo->vidrio_id = $id;
$modelo->color = $_POST['color'];
# Llama al mtodo correspondiente (necesita guardar el objeto)
$modelo->save();
# Pasa de largo a la vista y en cambio, recurre a otro recurso
$this->listar();
}
function eliminar($id=0) {
# Instancia al modelo
$modelo = new Vidrio();
# Modifica las propiedades necesarias
$modelo->vidrio_id = (int)$id;
# Llama al mtodo correspondiente (necesita destruir al objeto)
$modelo->destroy();
# Pasa de largo a la vista y en cambio, recurre a otro recurso
$this->listar();
}
function listar() {
# Recurre al colector para traer toda la coleccin de objetos Vidrio
$coleccion = VidrioCollector::get();
# Le entrega la informacin a la vista
$vista = new VidrioView();
$vista->mostrar_listado($coleccion);
}
}