MVC, está formado por tres niveles:
- El Modelo representa la información con la que trabaja la aplicación, es decir, su lógica de negocio.
- La Vista transforma el modelo en una página web que permite al usuario interactuar con ella.
- El Controlador se encarga de procesar las interacciones del usuario y realiza los cambios apropiados en el modelo o en la vista.
La arquitectura MVC separa la lógica de negocio (el modelo) y la presentación (la vista) por lo que se consigue un mantenimiento más sencillo de las aplicaciones. Si por ejemplo una misma aplicación debe ejecutarse tanto en un navegador estándar como un un navegador de un dispositivo móvil, solamente es necesario crear una vista nueva para cada dispositivo; manteniendo el controlador y el modelo original. El controlador se encarga de aislar al modelo y a la vista de los detalles del protocolo utilizado para las peticiones (HTTP, consola de comandos, email, etc.). El modelo se encarga de la abstracción de la lógica relacionada con los datos, haciendo que la vista y las acciones sean independientes de, por ejemplo, el tipo de gestor de bases de datos utilizado por la aplicación.
Las capas de la arquitectura MVC
Para poder entender las ventajas de utilizar el patrón MVC, se va a transformar una aplicación simple realizada con PHP en una aplicación que sigue la arquitectura MVC. Un buen ejemplo para ilustrar esta explicación es el de mostrar una lista con las últimas entradas o artículos de un blog.
Programación simple
Utilizando solamente PHP normal y corriente, el script necesario para mostrar los artículos almacenados en una base de datos se muestra en el siguiente listado:
Un script simple
// Conectar con la base de datos y seleccionarla $conexion = mysql_connect('localhost', 'miusuario', 'micontrasena'); mysql_select_db('blog_db', $conexion); // Ejecutar la consulta SQL $resultado = mysql_query('SELECT fecha, titulo FROM articulo', $conexion); ?>Listado de Artículos
// Mostrar los resultados con HTML while ($fila = mysql_fetch_array($resultado, MYSQL_ASSOC)) { echo "\t\n"; printf("\t\t\n", $fila['fecha']); printf("\t\t\n", $fila['titulo']); echo"\t\n"; }?>// Cerrar la conexion mysql_close($conexion); ?>
Fecha Titulo %s %s
El script anterior es fácil de escribir y rápido de ejecutar, pero muy difícil de mantener y actualizar. Los principales problemas del código anterior son:
- No existe protección frente a errores (¿qué ocurre si falla la conexión con la base de datos?).
- El código HTML y el código PHP están mezclados en el mismo archivo e incluso en algunas partes están entrelazados.
- El código solo funciona si la base de datos es MySQL.
Separando la presentación
Las llamadas a
echo
y printf
del listado 2-1 dificultan la lectura del código. De hecho, modificar el código HTML del script anterior para mejorar la presentación es un follón debido a cómo está programado. Así que el código va a ser dividido en dos partes. En primer lugar, el códigoPHP puro con toda la lógica de negocio se incluye en el script del controlador, como se muestra en el listado 2-2.La parte del controlador, en
index.php
// Conectar con la base de datos y seleccionarla
$conexion = mysql_connect('localhost', 'miusuario', 'micontrasena');
mysql_select_db('blog_db', $conexion);
// Ejecutar la consulta SQL
$resultado = mysql_query('SELECT fecha, titulo FROM articulo', $conexion);
// Crear el array de elementos para la capa de la vista
$articulos = array();
while ($fila = mysql_fetch_array($resultado, MYSQL_ASSOC))
{
$articulos[] = $fila;
}
// Cerrar la conexión
mysql_close($conexion);
// Incluir la lógica de la vista
require('vista.php');
El código HTML, que contiene cierto código PHP a modo de plantilla, se almacena en el script de la vista, como se muestra en el listado 2-3.
La parte de la vista, en
vista.php
Listado de Artículos
foreach ($articulos as $articulo): ?> endforeach; ?>
Fecha | Título |
---|---|
echo $articulo['fecha'] ?> | echo $articulo['titulo'] ?> |
Una buena regla general para determinar si la parte de la vista está suficientemente limpia de código es que debería contener una cantidad mínima de código PHP, la suficiente como para que un diseñador HTML sin conocimientos de PHP pueda entenderla. Las instrucciones más comunes en la parte de la vista suelen ser
echo
, if/endif
, foreach/endforeach
y poco más. Además, no se deben incluir instrucciones PHP que generen etiquetas HTML.Toda la lógica se ha centralizado en el script del controlador, que solamente contiene códigoPHP y ningún tipo de HTML. De hecho, y como puedes imaginar, el mismo controlador se puede reutilizar para otros tipos de presentaciones completamente diferentes, como por ejemplo un archivo PDF o una estructura de tipo XML.
Separando la manipulación de los datos
La mayor parte del script del controlador se encarga de la manipulación de los datos. Pero, ¿qué ocurre si se necesita la lista de entradas del blog para otro controlador, por ejemplo uno que se dedica a generar el canal RSS de las entradas del blog? ¿Y si se quieren centralizar todas las consultas a la base de datos en un único sitio para evitar duplicidades? ¿Qué ocurre si cambia el modelo de datos y la tabla
articulo
pasa a llamarse articulo_blog
? ¿Y si se quiere cambiar a PostgreSQL en vez de MySQL? Para poder hacer todo esto, es imprescindible eliminar del controlador todo el código que se encarga de la manipulación de los datos y ponerlo en otro script, llamado el modelo, tal y como se muestra en el listado 2-4.La parte del modelo, en
modelo.php
function getTodosLosArticulos()
{
// Conectar con la base de datos y seleccionarla
$conexion = mysql_connect('localhost', 'miusuario', 'micontrasena');
mysql_select_db('blog_db', $conexion);
// Ejecutar la consulta SQL
$resultado = mysql_query('SELECT fecha, titulo FROM articulo', $conexion);
// Crear el array de elementos para la capa de la vista
$articulos = array();
while ($fila = mysql_fetch_array($resultado, MYSQL_ASSOC))
{
$articulos[] = $fila;
}
// Cerrar la conexión
mysql_close($conexion);
return $articulos;
}
El controlador modificado se puede ver en el listado 2-5.
La parte del controlador, modificada, en
index.php
// Incluir la lógica del modelo
require_once('modelo.php');
// Obtener la lista de artículos
$articulos = getTodosLosArticulos();
// Incluir la lógica de la vista
require('vista.php');
Ahora el controlador es mucho más fácil de leer. Su única tarea es la de obtener los datos del modelo y pasárselos a la vista. En las aplicaciones más complejas, el controlador se encarga además de procesar las peticiones, las sesiones de los usuarios, la autenticación, etc. El uso de nombres apropiados para las funciones del modelo hacen que sea innecesario añadir comentarios al código del controlador.
El script del modelo solamente se encarga del acceso a los datos y puede ser reorganizado a tal efecto. Todos los parámetros que no dependen de la capa de datos (como por ejemplo los parámetros de la petición del usuario) se deben obtener a través del controlador y por tanto, no se puede acceder a ellos directamente desde el modelo. Las funciones del modelo se pueden reutilizar fácilmente en otros controladores.
Separación en capas más allá del MVC
El principio más importante de la arquitectura MVC es la separación del código del programa en tres capas, dependiendo de su naturaleza. La lógica relacionada con los datos se incluye en el modelo, el código de la presentación en la vista y la lógica de la aplicación en el controlador.
La programación se puede simplificar si se utilizan otros patrones de diseño. De esta forma, las capas del modelo, la vista y el controlador se pueden subidividir en más capas.
Abstracción de la base de datos
La capa del modelo se puede dividir en la capa de acceso a los datos y en la capa de abstracción de la base de datos. De esta forma, las funciones que acceden a los datos no utilizan sentencias ni consultas que dependen de una base de datos, sino que utilizan otras funciones para realizar las consultas. Así, si se cambia de sistema gestor de bases de datos, solamente es necesario actualizar la capa de abstracción de la base de datos.
El listado muestra un ejemplo de capa de abstracción de la base de datos y el listado 2-7 muestra una capa de acceso a datos específica para MySQL.
La parte del modelo correspondiente a la abstracción de la base de datos
function crear_conexion($servidor, $usuario, $contrasena)
{
return mysql_connect($servidor, $usuario, $contrasena);
}
function cerrar_conexion($conexion)
{
mysql_close($conexion);
}
function consulta_base_de_datos($consulta, $base_datos, $conexion)
{
mysql_select_db($base_datos, $conexion);
return mysql_query($consulta, $conexion);
}
function obtener_resultados($resultado)
{
return mysql_fetch_array($resultado, MYSQL_ASSOC);
}
La parte del modelo correspondiente al acceso a los datos
function getTodosLosArticulos() { // Conectar con la base de datos $conexion = crear_conexion('localhost', 'miusuario', 'micontrasena'); // Ejecutar la consulta SQL $resultado = consulta_base_de_datos('SELECT fecha, titulo FROM articulo', 'blog_db', $conexion); // Crear el array de elementos para la capa de la vista $articulos = array(); while ($fila = obtener_resultados($resultado)) { $articulos[] = $fila; } // Cerrar la conexión cerrar_conexion($conexion); return $articulos; }
Como se puede comprobar, la capa de acceso a datos no contiene funciones dependientes de ningún sistema gestor de bases de datos, por lo que es independiente de la base de datos utilizada. Además, las funciones creadas en la capa de abstracción de la base de datos se pueden reutilizar en otras funciones del modelo que necesiten acceder a la base de datos.
Los elementos de la vista
La capa de la vista también puede aprovechar la separación de código. Las páginas web suelen contener elementos que se muestran de forma idéntica a lo largo de toda la aplicación: cabeceras de la página, el layout genérico, el pie de página y la navegación global. Normalmente sólo cambia el interior de la página. Por este motivo, la vista se separa en un layout y en una plantilla. Normalmente, el layout es global en toda la aplicación o al menos en un grupo de páginas. La plantilla sólo se encarga de visualizar las variables definidas en el controlador. Para que estos componentes interaccionen entre sí correctamente, es necesario añadir cierto código. Siguiendo estos principios, la parte de la vista del listado, se puede separar en tres partes, como se muestra.
La parte de la plantilla de la vista, en
miplantilla.php
Listado de Artículos
foreach ($articulos as $articulo): ?> endforeach; ?>
Fecha | Título |
---|---|
echo $articulo['fecha'] ?> | echo $articulo['titulo'] ?> |
La parte de la lógica de la vista
$titulo = 'Listado de Artículos';
$contenido = include('miplantilla.php');
La parte del layout de la vista
echo $contenido ?>
Acciones y controlador frontal
En el ejemplo anterior, el controlador no se encargaba de realizar muchas tareas, pero en las aplicaciones web reales el controlador suele tener mucho trabajo. Una parte importante de su trabajo es común a todos los controladores de la aplicación. Entre las tareas comunes se encuentran el manejo de las peticiones del usuario, el manejo de la seguridad, cargar la configuración de la aplicación y otras tareas similares. Por este motivo, el controlador normalmente se divide en un controlador frontal, que es único para cada aplicación, y las acciones, que incluyen el código específico del controlador de cada página.
Una de las principales ventajas de utilizar un controlador frontal es que ofrece un punto de entrada único para toda la aplicación. Así, en caso de que sea necesario impedir el acceso a la aplicación, solamente es necesario editar el script correspondiente al controlador frontal. Si la aplicación no dispone de controlador frontal, se debería modificar cada uno de los controladores.
Orientación a objetos
Los ejemplos anteriores utilizan la programación procedimental. Las posibilidades que ofrecen los lenguajes de programación modernos para trabajar con objetos permiten simplificar la programación, ya que los objetos pueden encapsular la lógica, pueden heredar métodos y atributos entre diferentes objetos y proporcionan una serie de convenciones claras sobre la forma de nombrar a los objetos.
La implementación de una arquitectura MVC en un lenguaje de programación que no está orientado a objetos puede encontrarse con problemas de namespaces y código duplicado, dificultando la lectura del código de la aplicación.
La orientación a objetos permite a los desarrolladores trabajar con objetos de la vista, objetos del controlador y clases del modelo, transformando las funciones de los ejemplos anteriores en métodos. Se trata de un requisito obligatorio para las arquitecturas de tipo MVC.