En este capítulo, crearemos la típica aplicación de blog utilizando sólo PHP simple.
Para empezar, crea una página que muestre las entradas del blog que se han persistido en la base de datos. Escribirla en PHP simple es rápido, pero un poco sucio:
<?php
// index.php
$link = mysql_connect('localhost', 'myuser', 'mypassword');
mysql_select_db('blog_db', $link);
$result = mysql_query('SELECT id, title FROM post', $link);
?>
<html>
<head>
<title>List of Posts</title>
</head>
<body>
<h1>List of Posts</h1>
<ul>
<?php while ($row = mysql_fetch_assoc($result)): ?>
<li>
<a href="/show.php?id=<?php echo $row['id'] ?>">
<?php echo $row['title'] ?>
</a>
</li>
<?php endwhile; ?>
</ul>
</body>
</html>
<?php
mysql_close($link);
El código anterior es fácil de escribir y se ejecuta muy rápido, pero en cuanto la aplicación crece, es muy difícil de mantener. Sus principales problemas son los siguientes:
- No hay comprobación de errores: ¿qué sucede si falla la conexión a la base de datos?
- Organización deficiente: si la aplicación crece, este único archivo cada vez será más difícil de mantener, hasta que finalmente sea imposible. ¿Dónde se debe colocar el código para manejar los formularios? ¿Cómo se pueden validar los datos? ¿Dónde debe ir el código para enviar mensajes de correo electrónico?
- Es difícil reutilizar el código: ya que todo está en un archivo, no hay manera de volver a utilizar alguna parte de la aplicación en otras páginas del blog.
Nota Otro problema no mencionado aquí es el hecho de que la base de datos está vinculada a MySQL. Aunque no se ha tratado aquí, Symfony2 integra Doctrine, una librería que permite abstraer el acceso a la base de datos y el manejo de la información.
Vamos a trabajar a continuación en la solución de estos y muchos otros problemas más.
2.1.1. Aislando la parte de la vista
El código se puede mejorar fácilmente separando la lógica de la aplicación (código PHP puro) y "la parte de la vista" o "presentación", que está formada por todo lo relacionado con el código HTML:
<?php
// index.php
$link = mysql_connect('localhost', 'myuser', 'mypassword');
mysql_select_db('blog_db', $link);
$result = mysql_query('SELECT id, title FROM post', $link);
$posts = array();
while ($row = mysql_fetch_assoc($result)) {
$posts[] = $row;
}
mysql_close($link);
// incluye el código HTML de la vista
require 'templates/list.php';
Ahora el código HTML está guardado en un archivo separado (templates/list.php
). Este archivo contiene todo el código HTML junto con algunas pocas instrucciones PHP que utilizan la sintaxis alternativa recomendada para las plantillas PHP:
<html>
<head>
<title>List of Posts</title>
</head>
<body>
<h1>List of Posts</h1>
<ul>
<?php foreach ($posts as $post): ?>
<li>
<a href="/read?id=<?php echo $post['id'] ?>">
<?php echo $post['title'] ?>
</a>
</li>
<?php endforeach; ?>
</ul>
</body>
</html>
Por convención, el archivo que contiene toda la lógica de la aplicación (index.php
) se conoce como "controlador". El término controlador es una palabra que se utiliza mucho, independientemente del lenguaje o plataforma que utilices. Simplemente se refiere a la zona de tu código que procesa la petición del usuario y prepara la respuesta.
En este caso, nuestro controlador obtiene los datos de la base de datos y, luego los incluye en una plantilla para presentarlos al usuario. Con el controlador aislado, podríamos cambiar fácilmente sólo el archivo de la plantilla si queremos servir los contenidos del blog en otro formato (por ejemplo, list.json.php
para el formato JSON).
2.1.2. Aislando la lógica de la aplicación (el dominio)
Por ahora la aplicación sólo contiene una página. Pero ¿qué pasa si una segunda página necesita utilizar la misma conexión a la base de datos, e incluso el mismo resultado de la búsqueda de las entradas del blog? La solución consiste en refactorizar el código para que todas estas funciones básicas de acceso a datos de la aplicación estén aisladas en un nuevo archivo llamado model.php
:
<?php
// model.php
function open_database_connection()
{
$link = mysql_connect('localhost', 'myuser', 'mypassword');
mysql_select_db('blog_db', $link);
return $link;
}
function close_database_connection($link)
{
mysql_close($link);
}
function get_all_posts()
{
$link = open_database_connection();
$result = mysql_query('SELECT id, title FROM post', $link);
$posts = array();
while ($row = mysql_fetch_assoc($result)) {
$posts[] = $row;
}
close_database_connection($link);
return $posts;
}
Truco Se utiliza el nombre model.php
para el archivo porque el acceso a la lógica y los datos de una aplicación se conoce tradicionalmente como la capa del "modelo". En una aplicación bien organizada, la mayoría del código que representa tu "lógica de negocio" debe estar en el modelo (en lugar del controlador). Y, a diferencia de este ejemplo, sólo una parte (o ninguna) del modelo realmente está interesada en acceder a la base de datos.
El controlador (index.php
) ahora es muy sencillo:
<?php
require_once 'model.php';
$posts = get_all_posts();
require 'templates/list.php';
Ahora, la única tarea del controlador es conseguir los datos de la capa del modelo de la aplicación (el modelo) y utilizar una plantilla para mostrar los datos.
Este es un ejemplo muy simple del patrón modelo - vista - controlador.
2.1.3. Aislando el diseño
La aplicación ahora está dividida en tres partes distintas, lo que nos ofrece varias ventajas y la oportunidad de volver a utilizar casi todo en diferentes páginas.
La única parte del código que no se puede reutilizar es el diseño HTML + CSS de la página. Vamos a solucionar este problema creando un nuevo archivo llamado base.php
:
<!-- templates/base.php -->
<html>
<head>
<title><?php echo $title ?></title>
</head>
<body>
<?php echo $content ?>
</body>
</html>
La plantilla original (templates/list.php
) ahora se puede simplificar para que utilice el archivo base.php
anterior como base:
<?php $title = 'List of Posts' ?>
<?php ob_start() ?>
<h1>List of Posts</h1>
<ul>
<?php foreach ($posts as $post): ?>
<li>
<a href="/read?id=<?php echo $post['id'] ?>">
<?php echo $post['title'] ?>
</a>
</li>
<?php endforeach; ?>
</ul>
<?php $content = ob_get_clean() ?>
<?php include 'base.php' ?>
El código anterior introducido una metodología que nos permite reutilizar el diseño. Desafortunadamente, para conseguirlo estamos obligados a utilizar en la plantilla algunas funciones de PHP poco recomendables (ob_start()
, ob_get_clean()
). Symfony2 utiliza un componente de plantillas (Templating
) que nos permite realizar esto de una manera más limpia y sencilla. En breve lo verás en acción.