sábado, 1 de marzo de 2008

Symfony vs Zend Framework. 3- Turno de Zend Framework

Artículos de la serie:
  1. Definición del problema
  2. Comencemos con Symfony
  3. Turno de Zend Framework
En el capítulo anterior vimos como implementar nuestra aplicación mediante Symfony, ahora llega el turno de Zend Framework (ZF)

ZF es bastante reciente y ha tenido un desarrollo muy intenso desde su concepción. En el momento de escribir estas líneas tenemos disponible la RC1 de la versión 1.5. Una de las novedades anunciadas a finales del año pasado fue la inclusión de comandos para facilitar la creación del modelo de al aplicación, etc. pero parece que finalmente no va a estar disponible o, al menos no he encontrado referencias posteriores sobre ello.

Documentándonos

Como siempre lo primero es buscar información. ZF no se queda cojo en cuanto a recursos en este aspecto: podemos recurrir a la web oficial, donde encontraremos accesos a la documentación del API, la guía de referencia, etc. También es interesante vigilar Zend developer Zone ya que podremos encontrar tutoriales sobre el framework.

Como nota curiosa destacar que muchas de las personas envueltas en su desarrollo cuentan con blogs interesantes en los que abordan, por ejemplo, características futuras.

De todos modos para nuestro experimento nos hemos basado en un tutorial bastante interesante en el que se ejemplifica como hacer una herramienta en la que dar de alta álbumes musicales. Lo interesante es que hasta ahora este tutorial se a venido actualizando con cada nueva versión de ZF. En la actualidad se corresponde con la versión 1.4.5.

http://akrabat.com/wp-content/uploads/getting-started-with-the-zend-framework_145.pdf


Preparativos

Antes de comenzar con el desarrollo tenemos que asegurarnos que nuestro apache tenga cargado modulo rewrite y permita el uso del mismo, así que buscaremos por nuestro httpd.conf y tal vez tengamos que descomentar la linea en la que aparece:

LoadModule rewrite_module modules/mod_rewrite.so (dependiendo de la version de apache)
También nos aseguraremos de tener la directiva AllowOverride All

Siguiendo el tutorial mencionado, vamos creando los directorios necesarios con los archivos .htaccess que básicamente nos serviran para prohibir el acceso al directorio o, en otros casos, para desactivar el modulo rewrite de Apache.

Base de datos

Nuestra base de datos fue creada durante el capítulo anterior así que nos ahorraremos este paso. De todos modos, en caso cotnrario el proceso sería el mismo al que la mayoría estamos acostumbrados: optaríamos por diseñar en phpMyAdmin, Mysql Workbench o similar.

Bootstrap

De nuevo siguiendo el tutorial no tenemos mas que copiar un poco de código para llegar a tener nuestro index.php despachando todas las peticiones en el directorio raíz de nuestra aplicación. En nuestro caso finalmente quedo así:
<?php
error_reporting
(E_ALL|E_STRICT);
date_default_timezone_set('Europe/London');
set_include_path('.' . PATH_SEPARATOR . './library'

. PATH_SEPARATOR . './application/models/'. PATH_SEPARATOR . get_include_path());
include
"Zend/Loader.php";
Zend_Loader::loadClass('Zend_Controller_Front');

Zend_Loader::loadClass('Zend_Config_Ini');
Zend_Loader::loadClass('Zend_Registry');
Zend_Loader::loadClass('Zend_Db');

Zend_Loader::loadClass('Zend_Db_Table');
// load configuration
$config = new Zend_Config_Ini('./application/config.ini', 'general');

$registry = Zend_Registry::getInstance();
$registry->set('config', $config);
// setup database
$db = Zend_Db::factory($config->db->adapter,
$config->db->config->toArray());
Zend_Db_Table::setDefaultAdapter($db);

// setup controller
$frontController = Zend_Controller_Front::getInstance();
$frontController->throwExceptions(true);
$frontController->setBaseUrl('/zf/bloggart/');
$frontController->setControllerDirectory('./application/controllers');
// run!
$frontController->dispatch();


Vemos que queda estructurado en una primera zona donde se cargan las clases a utilizar mediante la instrucción

Zend_Loader
::loadClass('miclase');


A continuación se carga y se registra un pequeño archivo de configuración (config.ini) que en nuestro caso sólo inicializa el acceso a la base de datos.

$config = new Zend_Config_Ini('./application/config.ini', 'general');

En tercer lugar viene la instanciación de la base de datos

$db = Zend_Db::factory($config->db->adapter,
$config->db->config->toArray());
Zend_Db_Table::setDefaultAdapter($db);


Después se prepara el ojeto controlador de la aplicación indicado el directorio raíz de la misma y el directorio donde encontraremos los archivos controloadores.

$frontController = Zend_Controller_Front::getInstance();
$frontController->throwExceptions(true);
$frontController->setBaseUrl('/zf/bloggart/');
$frontController->setControllerDirectory('./application/controllers');


Por ultimo escribiremos la instrucción que despacha las peticiones a nuestra aplicación.

$frontController->dispatch();

Hasta aquí podríamos haber agradecido algún comando que nos hiciera casi todo esto ya que parece algo rutinario y mecánico. Por otro lado, al no tener esa ayuda, obtenemos una gran flexibilidad a la hora de definir nuestra estructura de directorios.

Modelo y acciones

Nuestro modelo se encontrará en /application/models y tendrá dos archivos: Articulo.php y Comentario.php Una vez más nadie habrá creado por nosotros esos archivos pero para nuestros propósitos serán muy simples. En el caso de Articulo.php será:

<?php
class Articulo extends Zend_Db_Table
{
protected
$_name = 'blog_articulo';
}
?>

y Comentario.php será análogo.

Básicamente estamos creando las clases que contendrán todos los datos de las tablas de la base de datos del mismo nombre. Conseguimos esto haciendo que hereden de la clase Zend_Db_Table que por supuesto es nativa de ZF.

En cuanto a las acciones, quedará definidas dentro de /application/controllers/ Para el ejemplo se han creado IndexController.php y ComentarioController.php. El primero corresponde a las acciones que muestran, crean, modifican y borran artículos. El segundo hace el mismo papel pero para los comentarios. Como ejemplo aquí está el código de las acciones del index:
<?php
class IndexController extends Zend_Controller_Action
{
function
init()
{
$this->view->baseUrl = $this->_request->getBaseUrl();
Zend_Loader::loadClass('Articulo');
}
function
indexAction()
{
$this->view->title = "Janfri Bloggart";
$articulo = new Articulo();
$this->view->articulos = $articulo->fetchAll(null,'idarticulo desc');
}

function
addAction()
{
$this->view->title = "Añadir artículo nuevo";
if (
$this->_request->isPost()) {
Zend_Loader::loadClass('Zend_Filter_StripTags');
$filter = new Zend_Filter_StripTags();
$titulo = $filter->filter($this->_request->getPost('titulo'));
$titulo = trim($titulo);
$cuerpo = trim($filter->filter($this->_request->getPost('cuerpo')));
if (
$titulo != '' && $cuerpo != '') {
$data = array(
'titulo' => $titulo,
'cuerpo' => $cuerpo,
);
$articulo = new Articulo();
$articulo->insert($data);
$this->_redirect('/');

return;
}
}

$articulo = new Articulo();
$this->view->articulo = $articulo->createRow();
// additional view fields required by form
$this->view->action = 'add';
$this->view->buttonText = 'Add';
}

function
editAction()
{
$this->view->title = "Editar artículo";
$articulo = new Articulo();
if (
$this->_request->isPost()) {
Zend_Loader::loadClass('Zend_Filter_StripTags');
$filter = new Zend_Filter_StripTags();
$idarticulo = (int)$this->_request->getPost('idarticulo');
$titulo = $filter->filter($this->_request->getPost('titulo'));
$titulo = trim($titulo);
$cuerpo = trim($filter->filter($this->_request->getPost('cuerpo')));
if (
$idarticulo !== false) {
if (
$titulo != '' && $cuerpo != '') {
$data = array(
'titulo' => $titulo,
'cuerpo' => $cuerpo,
);
$where = 'idarticulo = ' . $idarticulo;
$articulo->update($data, $where);
$this->_redirect('/');
return;
} else {
$this->view->articulo = $articulo->fetchRow('idarticulo='.$idarticulo);
}
}
} else {
// album id should be $params['id']
$idarticulo = (int)$this->_request->getParam('id', 0);

if (
$idarticulo > 0) {
$this->view->articulo = $articulo->fetchRow('idarticulo='.$idarticulo);
}
}
// additional view fields required by form
$this->view->action = 'edit';
$this->view->buttonText = 'Update';
}

}

Las plantillas

Ahora convertiremos nuestro único archvo HTML en plantillas .phtml que colocaremos en la ruta /application/views/ El trabajo a realizar es muy parecido al que se hizo en el capítulo anterior:
  • Preparamos dos plantillas para la cabecera y el pie de cada página.
  • Una plantilla para el bloque lateral derecho que en una aplicación real tendríamos que trabajar más, pero aquí queda como código estático.
  • Para organizarnos creamos un directorio para las plantillas relacionadas con los artículo y otro para los comentario. En cada uno de ellos tendremos index.phtml, add.phtml, edit.phtml y delete.phtml. Además las plantillas de creación y edición llaman a otra auxiliar que contiene única y exclusivamente el html relativo al formulario.
Como muestra aquí tenemos:

index.phtml
<?php echo $this->render('header.phtml'); ?>
<div id="content">
<?php foreach($this->articulos as $articulo) : ?>
<div class="post">
<h2 class="title"><a href="#"><?php echo $this->escape($articulo->titulo);?></a></h2>
<div class="entry"><?php echo $this->escape($articulo->cuerpo);?></div>
<p class="meta"><span class="posted">Posted by <a href="#">Someone</a>
on December 17, 2007</span> <a href="<?php echo $this->baseUrl; ?>/index/edit/id/<?php echo $articulo->idarticulo;?>" class="permalink">Editar</a>
<a href="<?php echo $this->baseUrl; ?>/comentario/index/id/<?php echo $articulo->idarticulo;?>" class="comentarios">Commentarios</a></p>
</div>
<a href="<?php echo $this->baseUrl; ?>/index/delete/id/<?php echo $articulo->idarticulo;?>">Delete</a>
<?php endforeach; ?></div>
<!-- end #content -->
<?php echo $this->render('lateral.phtml'); ?>
<p><a href="<?php echo $this->baseUrl; ?>/index/add">Añadir nuevo artículo</a></p>
<?php echo $this->render('footer.phtml'); ?>


Como se puede observar usamos $this-&gt;render($plantilla); para incluir el código de otra plantilla.

add.phtml que es exáctamente igual que edit.phtml



<?php echo $this->render('header.phtml'); ?>
<div id="content">
<h2><?php echo $this->escape($this->title); ?></h2>
<?php echo $this->render('index/_form.phtml'); ?>
</div>
<!-- end #content -->
<?php echo $this->render('lateral.phtml'); ?>
<?php
echo $this->render('footer.phtml'); ?>

y por último _form.phtml

<form action="<?php echo $this->baseUrl ?>/index/<?php
echo $this->action; ?>" method="post">
<div>
<label for="artist">Título</label>
<input type="text" name="titulo"
value="<?php echo $this->escape(trim($this->articulo->titulo));?>"/>
</div>
<div>
<label for="title">Cuerpo</label>
<textarea name="cuerpo" cols="60" rows="30">
<?php echo $this->escape($this->articulo->cuerpo);?>
</textarea>
</div>
<div id="formbutton">
<input type="hidden" name="idarticulo" value="<?php echo $this->articulo->idarticulo; ?>" />
<input type="submit" name="add"
value="<?php echo $this->escape($this->buttonText); ?>" />
</div>
</form>



Conclusiones

Una de las cosas que más me ha llamado la atención sobre ZF es su capacidad para ofrecer alternativas para hacer una misma cosa. Por ejemplo nos encontraremos con que podremos instanciar clases llamando en su constructor a unos cuantos parámetros o sustituyendo a estos por un único parámetro en forma de array asociativo que habremos preparado previamente.

La desventaja de no estar asistidos por comandos de shell se convierte en un punto a favor de la flexibilidad de la jerarquía de archivos, es decir, tendremos total poder de decisión para estructurar los archivos de nuestra aplicación.

Al final la forma de trabajo termina siendo bastante parecida a nuestro caso anterior, nos queda todo bastante estructurado según los principios del patrón MVC.

De nuevo nos quedamos con las sensación de haber rascado superficialmente algo que podría llegar a ser muy potente en caso de tener que hacer una aplicación más compleja.

No hay comentarios: