Por favor, haz un clic sobre los anuncios cuando pases por mi blog, ya? =)

Zend Framework 11: Sesiones, registro, $this->url(), multiples tablas y demases

8 comentarios
Esta es la parte número once de mi tutorial de ZF. La parte anterior la puedes leer acá.

1) Manejo de sesiones

Imagino que ya sabes qué son y  para qué se usan las sesiones. En forma breve, las sesiones son una forma de guardar en PHP datos globales a la sesión de un usuario en el servidor.

Para manejar sesiones en ZF, debemos hacer esto:

//inicia sesión
Zend_Session::start();

//creo un 'namespace', o bien devuelvo el objeto, si es que el namespace ya existe
$mysession = new Zend_Session_Namespace('mysession');
//internamente lo que hace es
//$mysession = $_SESSION['myNamespace']

En ZF se usan los namespaces para manejar datos de la sesion. Segun el manual oficial, lo hacen para hacer más ordenado el manejo de los datos de sesiones.

 Para guardar datos

Una vez creada la sesión, para guardar datos, en cualquier parte del código (acciones,controladores, plantillas, funciones propias) hago algo como esto:

//creo o recupero el namespace de nombre 'mysession'
 $mysession = new Zend_Session_Namespace('mysession');
 //duracion de datos de la sesion: 5 minutos de inactividad y se cierra la sesion
$mysession->setExpirationSeconds(300);

//de esta forma creo variables dentro de la sesion; si existen desde antes se modifican
//si no, se crean automaticamente, y se guardan los datos cuando los asigno
$mysession->usuario_id = $usuario['USUARIO_ID'];
$mysession->tipo_usuario = $usuario['TU_NOMBRE'];
$mysession->nombre_usuario = $usuario['USUARIO_NOMBRE'];




Ahora, para recuperar datos:

 $mysession = new Zend_Session_Namespace('mysession');
if (isset($mysession->saludo))
{
      $saludo = $mysession->saludo;
}

Para remover variables de la sesion:

$mysession = new Zend_Session_Namespace('mysession');
unset($mysession->usuario_id);
unset($mysession->tipo_usuario);

Y para destruir la sesión:

//destruyo todos los daots del namespace
Zend_Session::namespaceUnset('mysession');
Zend_Session::destroy();


2) Usando del Registro de ZF

ZF nos ofrece otra forma de almacenar datos, similar a las sesiones. Pero este está pensado para guardar objetos globales a la aplicación. Es decir, a diferencia de los objetos guardados en una sesión, que son solo pueden ser accedidos por la sesión, estos pueden ser accedidos desde todas las sesiones.

Ejemplo de uso:
//asigna un valor al registro
Zend_Registry::set('duracionSesion', $value);

//obtiene un valor del registro
$value = Zend_Registry::get('duracionSesion');

¿Para qué nos puede servir eso? Un ejemplo real en la próxima sección.

3) Consultas desde múltiples tablas


Imagino que han observado que en el sistema de ejemplo las consultas a las bases de datos son manejadas por las clases del modelo (las ubicadas en /application/models/BbTable/ ). Sin embargo, esas clases no me permiten hacer consultas que involucren a varias tablas. Para eso debo crear un objeto Zend_Db::factory() con los datos de conección de la base de datos.

Pero esto no será dificil, gracias a Zend_Registry() y nuestro archivo application.ini. En  /public/index.php podriamos añadir estas lineas, antes del $application = new Zend_Application(...) :

//cargo mi archivo de configuracion application.ini en el objeto $config, en la forma de un arreglo.
//Cargamos solo los valores de la sección 'production', como indica el segundo parámetro, pues en esa //sección hemos definido los parámetros de conección a la base de datos.
$config = new Zend_Config_Ini('../application/configs/application.ini', 'production');

//creo un objeto DB factory() con los datos de la seccion resources del archivo application.ini
$mydb = Zend_Db::factory('Pdo_Mysql', $config->resources->db->params);

//guardo en el registro el objeto DB Factory() creado, para poder usarlo despues en cualquier parte
Zend_Registry::set('mydb', $mydb);


 Cuando hacemos $config->resources->db->params, se devuelve un arreglo con todos los clave-valor listados bajo esa sección. Recordemos que en nuestro archivo application.ini en la sección producción tenemos:

resources.db.adapter = PDO_MYSQL
resources.db.params.host = localhost
resources.db.params.username = albumusuario
resources.db.params.password = 123
resources.db.params.dbname = albums

Entonces,
$arr = $config->resources->db->params;
nos devolverá:
{ 'host => 'localhost',  'username => 'albumusuario', 'password => '123', 'dbname => 'albums' }


Ahora, ¿cómo usamos el objeto factory para hacer consultas?

Simple, en cualquier parte del código donde debamos hacer una consulta a la base de datos, podremos hacer algo como:

//recupero objeto desde el registro
$db = Zend_Registry::get('mydb');

//opcional, esto es para que devuelva los resultados como objetos $row->campo
$db->setFetchMode(Zend_Db::FETCH_OBJ);

//hago la consulta sql que desee:
$row = $db->fetchRow("SELECT * from $tabla WHERE id = '$id' ;");

//insertar (arreglo_datos es un array del tipo 
//{ 'nombrecampo' => 'valor', 'nombreCampo2' => 'valor2', ...}
$db->insert($tabla, $arreglo_datos);

//actualizar
$n = $db->update($tabla, $arreglo_datos, 'id = "' . $id . '" ');

//borrar
$n = $db->delete($tabla, 'id = "' . $id . '" ');

//consulta con muchas tablas...
$sql = 'SELECT
        reserva.reserva_id as RESERVA_ID, reserva.reserva_estado as RESERVA_ESTADO,
        reserva.te_nombre as EQUIPO_TIPO, equipo.equipo_nombre as EQUIPO_NOMBRE,
        reserva.tr_nombre as TIPO_RESERVA, reserva.sala_nombre AS SALA_NOMBRE,
        reserva.cc_codigo AS CC_CODIGO, reserva.USUARIO_NOMBRE AS USUARIO_NOMBRE,
        reserva.USUARIO_ID AS USUARIO_ID, 
        DATE_FORMAT(reserva_fecha_reserva, "%d-%m-%Y") as FECHA_RESERVA,
        reserva.observaciones_usuario AS OBSERVACIONES_USUARIO,
        reserva.observaciones_operador AS OBSERVACIONES_OPERADOR,
        reserva.usuario_entrega AS USUARIO_ENTREGA,
        reserva.usuario_aprueba AS USUARIO_APRUEBA,
        reserva.usuario_retira AS USUARIO_RETIRA,
        reserva.usuario_mail AS USUARIO_MAIL,  
         DATE_FORMAT(reserva.bloque_desde, "%H:%i") as DESDE,
         DATE_FORMAT(reserva.bloque_hasta, "%H:%i") as HASTA
          
         FROM
           (select reserva_equipo.*, usuario.USUARIO_NOMBRE 
           from reserva_equipo, usuario
           where  reserva_equipo.usuario_id = usuario.usuario_id ';


  $sql .= ' ORDER BY reserva_equipo.reserva_fecha_reserva DESC )
            as reserva
        LEFT JOIN
         equipos equipo
         ON
          reserva.equipo_id = equipo.equipo_id   ;';

 $rows = $db->fetchAll($sql);

Como podemos ver, podemos hacer todo tipo de consultas usando el objeto factory.


4) Volver a usar los comandos de Zend Framework sin Netbeans

Si han seguido este tutorial usando Netbeans y ahora tratan de usar los comandos de ZF directamente desde la línea de comandos, notarán que les arroja un error:



Este error está relacionado con la clase NetBeansCommandProvider.php, pues el comando zf.bat no puede encontrarla cuando no lo llamamos desde NetBeans. En verdad la única forma que he descubierto de cómo arreglar esto es editando a mano el archivo de configuración de ZF, el que se escribió cuando hicimos

zf --setup config-file

Como dijimos anteriormente, "Esto crea un archivo llamado .zf.ini en nuestro directorio de usuario (en Windows esto es en  C:\Documents and Settings\tu_nombre_de_usuario y en Linux es en $HOME) donde guarda algunas preferencias personales para usar ZF como el include_path y la clase CommandProvider, que es necesaria para usar ZF dentro de Netbeans."


Si queremos volver a usar ZF sin netBeans, debemos abrir el archivo C:\Documents and Settings\tu_nombre_de_usuario\.zf.ini  y veremos algo parecido a esto:

php.include_path = ".;G:\xampp\xampp\php\PEAR;"
basicloader.classes.0 = "NetBeansCommandsProvider"

Si borramos la segunda línea, podremos usar ZF sin NetBeans. Eso sí, para volver a usarlo, deberemos volver a ir a  las opciones de NetBeans / PHP / Zend y presionar el botón 'Register Provider'... o bien volver a agregar la línea que borramos.

5) ¡¡¿Como #%@& borro controladores, acciones, formularios o tablas del modelo ?!!!

¿Les ha pasado? A mí, si. Creo un nuevo controlador, me equivoco al ponerle nombre y presiono enter, y zas! el controlador se ha creado. O creo una acción y se la asigno al controlador equivocado.
¿Y qué hago ahora? Pues la verdad es que ZF no tiene una opcion de línea de comandos para borrar clases del modelo. Y no podemos darnos el lujo de borrarlas directamente los archivos o de cambiarles el nombre, pues todas las clases del modelo estan referenciadas desde el archivo .zfproject.xml ubicado en la carpeta raíz del proyecto.

Así que si queremos borrarlas, lo que tendremos que hacer es abrir ese archivo y editarlo directamente. Por ejemplo, una parte de la sección de los controladores del proyecto que hemos creado se ve así:

<controllerFile controllerName="Index">
    <actionMethod actionName="index"/>
    <actionMethod actionName="prueba"/>
    <actionMethod actionName="saludoajax"/>
    <actionMethod actionName="saludoajax2"/>
</controllerFile>
<controllerFile controllerName="Error"/>
<controllerFile controllerName="Micontrolador">
    <actionMethod actionName="index"/>
</controllerFile>
<controllerFile controllerName="Album">
    <actionMethod actionName="index"/>
    <actionMethod actionName="add"/>
    <actionMethod actionName="delete"/>
    <actionMethod actionName="update"/>
    <actionMethod actionName="listadoajax"/>
</controllerFile>
<controllerFile controllerName="Artista">
    <actionMethod actionName="index"/>
    <actionMethod actionName="add"/>
    <actionMethod actionName="update"/>
    <actionMethod actionName="delete"/>
</controllerFile>

Creo que la estructura del archivo habla por sí misma. Basta cambiar o borrar las secciones correspondientes, y listo.

(sí, apreciado lector, yo también desearía una forma más sencilla de hacer esto...)

 6)Un caso especial con $this->url() al crear los links


Hay algo que parece trivial, pero es importante saberlo. Para  cuando creamos nuestros links con $this->url() en nuestros archivos .phtml de esta forma:

<a href="<?php echo $this->url(array('controller' => 'album',
   'action' => 'delete', 'id' => $d->id)); ?>">Delete</a>

Si bien recuerdan nuestro proyecto de ejemplo, este código creará un enlace al controlador Album, acción Delete, enviándole como parámetro id=[numero], algo como:

<a href="/miproyecto/album'/delete/id/56">Delete</a>

Sin embargo, $this->url() agrega automáticamente al enlace todos los parámetros con los que se llamó a la página actual. Es decir, si yo entro a una página, por ejempo a


http://localhost/miproyecto/public/micontrolador/miaccion/nombre/pepe/edad/15
(que es lo mismo que escribir
http://localhost/miproyecto/public/micontrolador/miaccion?nombre=pepe&edad=15 )

Y dentro de esa página tengo un enlace de esta forma:
<a href="<?php echo $this->url(array('controller' => 'album',
   'action' => 'delete', 'id' => $d->id)); ?>">Delete</a>

al mostrarse el enlace, este no será
<a href="/miproyecto/album'/delete/id/56">Delete</a>

sino
<a href="/miproyecto/album'/delete/id/56/nombre/pepe/edad/edad/15">Delete</a>

¿sorprendido? No creo que seas el único. Aunque tal vez a alguien se le ocurrió que este sería un comportamiento deseable, al menos yo no estoy de acuerdo. Para solucionar este problema, basta agregarle dos parámetro más a $this->url( $arrayparametros, $router, boolean noCopiarParams), de esta forma:

<a href="<?php echo $this->url(array('controller' => 'album',
    'action' => 'delete', 'id' => $d->id), "", true); ?>">Delete</a>


Y listo. Problema solucionado.

Nos vemos en el próximo artículo, en el que hablaremos acerca de usos avanzados de layout.phtml ¿Qé pasa, por ejemplo, si en el layout de mi página quiero colocar algunos pedazos de código que no son parte de ninguna vista? Por ejemplo, widgets de otras páginas, y que sean opcionales, según la página que estemos visitando. Eso en el próximo artículo.

8 comentarios :

Alejandro dijo...

Muchas gracias! Lo de sesión me sirvió mucho. Y lo del problema del netbeans con la consola de zend lo había resuelto antes, pero si hubiese visto tu post previamente tendría más pelos de los que me quedaron por la locura que me dejo

Angelorum dijo...

Alejandro: Pues si te fijas en mi foto de perfil del blog, verás que no tengo cabello XD

Anónimo dijo...

gracias por publicar estos ejemplos de zend .
tengo una consulta como puedo cargar archivos pdf a la base de datos utilizando zend_form_file
graciass....

ranfe dijo...

Amigo tengo un problema con la sesiones, cuando cambio de pagina se pierde la sesion que habia iniciado aunque intente recuperar la informacion con el namespace, hago todo lo que haces pero solo me funciona en la pagina donde cargue la sesion y me carga bien todo pero cuando entro a otra ya no, necesito ayuda por favor!

Anónimo dijo...

Antes de nada gracias por tu ayuda y por estos estupendos tutoriales

Comentas que: En /public/index.php podriamos añadir estas lineas, antes del $application = new Zend_Application(...) :

//cargo mi archivo de configuracion application.ini en el objeto $config, en la forma de un arreglo.
//Cargamos solo los valores de la sección 'production', como indica el segundo parámetro, pues en esa //sección hemos definido los parámetros de conección a la base de datos.
$config = new Zend_Config_Ini('../application/configs/application.ini', 'production');

//creo un objeto DB factory() con los datos de la seccion resources del archivo application.ini
$mydb = Zend_Db::factory('Pdo_Mysql', $config->resources->db->params);

//guardo en el registro el objeto DB Factory() creado, para poder usarlo despues en cualquier parte
Zend_Registry::set('mydb', $mydb);


Si las copio antes de : $application = new Zend_Application
me da error precisamente en $config = new Zend_Config_Ini('../application/configs/application.ini', 'production');

Dice algo asi como que no se encontro la libreria de zend_config_ini, sin embargo si la copio despues, no da error, aunque tampoco funciona...
Que se te ocurre?

Anónimo dijo...

una consulta amigo y como hago para que siempre este la session activa en el navegador, es decir una opcion que indique no cerrar session y cuando habra el navegador despues de cerrado la session ya este activa

Juan dijo...

Me pasa lo mismo que al del comentario de Mayo de 2012

Anónimo dijo...

para el error que te aparece solo fata agregar las siguientes lineas antes
require_once 'Zend/Config/Ini.php';
require_once 'Zend/Db.php';
require_once 'Zend/Registry.php';

mi problema es al momento de recorrer el array de la consulta no puedo hacer que los datos se impriman