Sin embargo no va a ser la primera ni la última vez que surjan dudas al respecto, “que esto no va en el modelo”, “que esto si va en el modelo”, etc. Sin embargo, me arriesgo a hacer una afirmación que puede ser fácilmente criticada: todo proceso de datos va en el modelo. Hay una regla en inglés que dice “skinny controllers, fat models”.

Básicamente la idea de esto es que el controlador sólo funcione como intermediario entre la vista y el modelo cuando sea necesario, que no realice ninguna otra actividad, que toda la lógica este contenida en el modelo. De esta manera, lograremos tener nuestro código mucho más organizado y limpio.

Dentro de un sistema MVC, los modelos contienen la lógica de negocio. Se encargan de la lectura/escritura de la información, así que estaremos trabajando con ellos constantemente. El controlador puede comunicarse con los modelos para pedir y modificar datos, mientras que la vista puede leer datos pero no puede escribir.

Zend_Db_Select

Zend_Db_Select nos provee una API orientada a objetos para crear SQL SELECT’s, permitiéndonos setear individualmente cada parte del query. No es obligatorio usarlo en nuestros modelos. Si es una consulta simple podemos escribir el select normalmente, aunque en consultas más complejas que necesitamos ir armando por partes va a ser de gran ayuda.

La forma más fácil de crear un select object es con el método select() del db adapter:

 
$select = $dbAdapter->select();

Hecho esto, veamos rápidamente los métodos más usados:

// setear el from 
$select->from("miTabla", "estaColumna"); 
// setear el from asignandole un alias a la tabla y especificando que campos queremos pedir 
$select->from(array("esteAlias" => "estaTabla"), array("esteCampo", "yEsteOtro")); 
// agrega una clausula AND al where 
$select->where("nombre = ?", $nombre); 
$select->where("activo = 1"); 
// agrega una clausula OR 
$select->orWhere("activo = 2"); 
$select->group("pais"); 
$select->joinLeft("otraTabla", "otraTabla.id = tabla.id"); 
$select->limit($count, $offset); 
$select->order("id DESC"); 

Por último, haciendo echo $select; obtenemos la consulta SQL correspondiente.

Sin embargo, el 99% de las veces que queramos armar un select vamos a estar dentro de un modelo, así que obtenemos una instancia con el método select() de Zend_Db_Table_Abstract:

 // en un modelo 
$select = $this->select();

Este select no será un Zend_Db_Select, sino que será una versión especial llamada Zend_Db_Table_Select, sin embargo todos los métodos nombrados anteriormente funcionan, con un par de agregados. Por un lado, automáticamente setea el from() adecuado según la tabla del modelo actual, así que ese paso podemos obviarlo. Por otro lado, por defecto no permite realizar joins a otras tablas, por lo que si lo hacemos obtendremos un error. Para evitar esto, al momento de hacer un join deberemos cambiar este comportamiento con el método setIntegrityCheck():

$select = $this->select()->setIntegrityCheck(false)->joinLeft(...);

y listo, problema solucionado!

Hecho este rápido repaso a Zend_Db_Select (los obligo a leer el manual para obtener algo más de info y ejemplos!) también hablaré un poco de Zend_Db_Expr:

Supongamos que hacemos esto:

$select->from($tabla, array("COUNT(*)", "nombre", "pais"));

a simple vista esta bien, pero cuando Zend arma el select se da cuenta que “COUNT(*)” no es un campo correspondiente a la tabla por lo que genera un error. Para este caso hacemos uso de Zend_Db_Expr:

$count = new Zend_Db_Expr("COUNT(*)"); 
$select->from($tabla, array($count, "nombre", "pais"));

Otra forma que tenemos de hacer esto mismo es escribiendo la expresión entre paréntesis, y Zend automáticamente lo convertirá a una Zend_Db_Expr:

$select->from($tabla, array("(COUNT(*))", "nombre", "pais"));

Como comentario final sobre el tema, cuando querramos usar un subquery en algún select, deberemos pasarlo como una Zend_Db_Expr.

Zend_Db_Table

Implementando el patrón Table Data Gateway, Zend_Db_Table representa las diferentes tablas de nuestra bdd. Es decir que, por cada tabla sobre la que queramos trabajar, tendremos un modelo que extienda de Zend_Db_Table_Abstract. Como veremos más adelante nos brinda diferentes métodos para realizar INSERT’s, UPDATE’s, y demás operaciones.

Supongamos que tenemos nuestra tabla “mdw_usuarios”, cuyo primary key es “id_usuario”, nuestro modelo quedaría de la siguiente manera:

 class Mdw_Model_Usuarios extends Zend_Db_Table_Abstract { 
protected $_name = "mdw_usuarios"; 
protected $_primary = "id_usuario"; 
} 
$usuarios = new Mdw_Model_Usuarios;</pre></div>
<p>Especificando los atributos $_name y $_primary ya podemos trabajar sobre dicha tabla. Si no completamos $_primary, ZF tratará de buscarlo. Si la tabla no tiene una primary key, no puede ser usada con Zend_Db_Table.</p>
<h3>Operaciones básicas</h3>
<div class="codigo"><pre>$data = array("nombre" => $nombre, "apellido" => $apellido, "email" => $email); 
$usuarios->insert($data);

Crear un insert es así de simple: simplemente le pasamos como parámetro un array cuyas claves son los nombres de los campos con su correspondiente valor.

Realizar un update es muy parecido solo que tenemos un segundo parámetro para especificar el where:

$data = array("email" => $nuevoEmail); 
$where = "id_usuario = 2"; 
$usuarios->update($data, $where);</pre></div>
<p>Por su parte para realizar un delete debemos especificar un where con los campos a eliminar:</p>
<div class="codigo"><pre>$where = "id_usuario = 3"; 
$usuarios->delete($where);

Obteniendo datos de la bdd

Tenemos varias formas de leer datos de la bdd. La primera es con el método find(), que busca filas por su primary key, así que ese es el único parámetro que tenemos que enviar.

Por ejemplo para leer la fila correspondiente a id_usuario = 1:

$rows = $usuarios->find(1);</pre></div>
<p>También podemos obtener datos de más de un usuarios, enviando un array:</p>
<div class="codigo"><pre>// id_usuario = 1, 2, 3 
$rows = $usuarios->find(array(1, 2, 3));</pre></div>
<p>Find() devuelve un objeto del tipo Zend_Db_Table_Rowset_Abstract sobre el que podemos trabajar directamente, por ejemplo lo podemos recorrer con un foreach:</p>
<div class="codigo"><pre>foreach ($rows as $row) { 
echo $row->nombre . "<br />"; 
}

Si quisieramos también podríamos obtener los datos en un array php:

$arrayRows = $rows->toArray(); 

La segunda forma de leer informacion es con el metodo fetchAll($where, $order, $count, $offset). Ningun parámetro es obligatorio, es decir que haciendo $model->fetchAll() obtendremos todos los registros de esa tabla.

$model->fetchAll("id_pais = 3"); // todos los usuarios cuyo id_pais = 3 
$model->fetchAll($select, "apellido ASC", 10); // 10 usuarios cuyo ud_pais = 3, ordenados por apellido, podemos pasar un Zend_Db_Select en el where 

El fetchAll() también nos devuelve un rowset.

Zend_Db_Table_Rowset y Zend_Db_Table_Row

Un Row corresponde a una fila de la base de datos, y un Rowset es un conjunto de Rows, por lo tanto representa un conjunto de filas de la base de datos. Al hacer una consulta, tanto find() como fetchAll() devuelven un Rowset con los resultados obtenidos, y al recorrer ese Rowset cada elemento es un Row.

Anteriormente vimos que el Rowset lo podemos recorrer con un foreach, convertirlo en array, etc. En cuanto al Row veremos algunas cosas más. Suponiendo que tenemos una columna llamada “id_usuario”, podremos acceder a su valor de la siguiente manera:

$row->id_usuario; 

Un Row implementa el patron Row Data Gateway, por lo que podemos crear, eliminar y actualizar datos directamente desde él:

$row->delete(); // elimina el registro de la bdd 
$row->nombre_usuario = 'Pepe'; // modificamos el nombre de usuario 
$row->save(); // y guardamos los cambios 
// para crear un nuevo registro 
$newRow = $this->createRow(); 
$this->nombre = "Usuario nuevo"; 
$this->edad = 14; 
$this->save(); 

Siguiendo con la guía:

Sí recién te enteras de la Guía Zend te recomiendo que revises el primer artículo de introducción y no te pierdas nuestras próximas publicaciones semanales. Comparte tus dudas e inquietudes en los comentarios.

Ir al siguiente capítulo: Guía Zend: Controladores, Front Controller Plugins y Action Helpers