Zend Framework/Zend Db/ABM Básico
Introducción
editarEsta es la configuración básica para un ABM. Aquí encontraremos un ejemplo sobre cómo estructurar un proyecto para la manipulación de una base de datos.
En esta sección se detallarán los puntos más importantes del ejemplo. Más abajo se encuentra el código completo del ejemplo.
A todos los invitamos a suscribirse al grupo Zend Framework Hispano. Ahí colocaremos los archivos de ejemplos para que puedan descargarlos. Podés bajar el código de este ejemplo aquí zft_db_abm_basico.zip
En este ejemplo accederemos hacia la base de datos world la cual se puede descargar desde http://downloads.mysql.com/docs/world.sql.gz, y la guía de instalación de la misma se puede encontrar en http://dev.mysql.com/doc/world-setup/en/world-setup.html. Podés encontrar otros detalles de la utilización de esta base de datos en Zend Framework/Zend Db/Configuración Básica.
Circuito de ejecución
editarPara empezar, al hacer click en el enlace 'world' se entra a la pantalla principal de la sección World, es decir, http://localhost/zft/html/world/index. Para ello, se llama al controlador #/application/controllers/WorldController.php, que ejecuta la acción del index indexAction().
// El index es por defecto el listado public function indexAction() { $city = new City(); $this->view->cities = $city->listCities(); }
Esta acción pide la lista de ciudades al modelo #/application/models/City.php mediante el método listCities(), la prepara guardándola en la variable $this->view->cities, y pasa estos datos a #/application/views/scripts/world/index.phtml.
<table border="1"> <tr> <th>ID</th> <th>Name</th> <th>CountryCode</th> <th>District</th> <th>Population</th> <th><a href="<?php echo $this->baseUrl() ?>/world/alta">Alta</a></th> </tr> <?php foreach ($this->cities as $city) {?> <tr> <td><?=$city->ID?></td> <td><?=$city->Name?></td> <td><?=$city->CountryCode?></td> <td><?=$city->District?></td> <td><?=$city->Population?></td> <td> <a href="<?php echo $this->baseUrl() ?>/world/modificacion/ID/<?=$city->ID?>">Modificacion</a> <a href="<?php echo $this->baseUrl() ?>/world/baja/ID/<?=$city->ID?>">Baja</a> </td> </tr> <?php }?> </table>
Allí se distribuyen los datos de la lista de ciudades obtenida en una tabla que se despliega en pantalla, y se brindan las opciones de realizar un alta,
<th><a href="<?php echo $this->baseUrl() ?>/world/alta">Alta</a></th>
una modificación,
<a href="<?php echo $this->baseUrl() ?>/world/modificacion/ID/<?=$city->ID?>">Modificacion</a>
o una baja.
<a href="<?php echo $this->baseUrl() ?>/world/baja/ID/<?=$city->ID?>">Baja</a>
Una vez en esta pantalla, si queremos realizar un alta, vamos al enlace de la celda superior derecha de la tabla.
Este enlace va a llamar al controlador #/application/controllers/WorldController.php nuevamente, pero esta vez va a ejecutar la acción de alta altaAction().
// Aqui solo es necesario el ingreso de datos public function altaAction() { }
En esta acción no se carga ningún dato porque no es necesario para el formulario que generará una nueva entrada en la tabla, entonces nos dirige directamente a #/application/views/scripts/world/alta.phtml.
<form name="alta" action="<?php echo $this->baseUrl() ?>/world/guardar" method="post"> <table> <tr> <td>Name</td> <td><input type="text" name="Name" value=""></td> </tr> <tr> <td>CountryCode</td> <td><input type="text" name="CountryCode" value=""></td> </tr> <tr> <td>District</td> <td><input type="text" name="District" value=""></td> </tr> <tr> <td>Population</td> <td><input type="text" name="Population" value=""></td> </tr> <tr> <td colspan="2"><input type="submit" name="guardar" value="Guardar"></td> </tr> </table> </form>
Allí se muestra el formulario a completar con los datos de la nueva entrada, excepto el ID, que se incrementará automáticamente respecto del último ID de la tabla, y también se muestra el botón para guardar los datos.
Este botón pasará por POST los datos ingresados, y le indicará al #/application/controllers/WorldController.php que ejecute la acción de guardar guardarAction().
public function guardarAction() { $city = new City(); // Si llega un ID por POST o GET, se guarda en esta variable. // Sino, se le asigna false (para determinar más adelante si se trata // de un alta o una modificación) $ID = $this->_request->getParam('ID',false); // Preparación de los datos para ser guardados $data = array( 'Name' => $this->_request->getParam('Name'), 'CountryCode' => $this->_request->getParam('CountryCode'), 'District' => $this->_request->getParam('District'), 'Population' => $this->_request->getParam('Population') ); // Si por POST o GET vino un ID, es porque se trata de una modificacion if($ID){ // Se realiza la actualización sobre la entrada seleccionada $where = $city->getAdapter()->quoteInto('ID = ?', $ID); $city->update($data, $where); // Sino, se trata de un alta }else{ // Se realiza el alta de una nueva entrada $city->insert($data); } }
Esta acción detectará si obtuvo como parámetro un ID o no.
if($ID){
Como en este caso no lo recibe, prepara los datos en el arreglo $data
$data = array( 'Name' => $this->_request->getParam('Name'), 'CountryCode' => $this->_request->getParam('CountryCode'), 'District' => $this->_request->getParam('District'), 'Population' => $this->_request->getParam('Population') );
y luego los inserta en la tabla de ciudades como una entrada nueva y un ID nuevo.
}else{ // Se realiza el alta de una nueva entrada $city->insert($data); }
Luego la acción nos dirige a #/application/views/scripts/world/guardar.phtml,
Alta/modificación realizada con éxito. <a href="<?php echo $this->baseUrl() ?>/world/index">Listado</a>
en donde se muestra un mensaje informando que el alta se realizó con éxito, y nos da un enlace para volver al listado de ciudades.
De nuevo en el listado de ciudades, si queremos realizar una modificación, hacemos click en el enlace de 'modificación' que esté en la misma fila de la entrada que queremos modificar.
Archivo:Zft db abm modificacion.png
Con este enlace se llamará al #/application/controllers/WorldController.php pasándole como parámetro el ID de la entrada a modificar a travez de GET,
<a href="<?php echo $this->baseUrl() ?>/world/modificacion/ID/<?=$city->ID?>">Modificacion</a>
el cual ejecutará la acción modificacionAction().
// Para modificar los valores de una entrada al hacer click en modificar public function modificacionAction() { $city = new City(); // Se obtiene los datos de la entrada a modificar mediante el ID $where = $city->getAdapter()->quoteInto('ID = ?', $this->_request->getParam('ID')); $this->view->city = $city->fetchAll($where); }
Con el ID que obtuvo por GET, esta acción le pedirá al modelo #/application/models/City.php la entrada que estamos por modificar, y la preparará en una variable para pasársela a #/application/views/script/world/modificacion.phtml.
<?php // Se guarda en esta variable los datos de la entrada a modificar $city = $this->city->current(); // Se cargan los datos de la entrada a modificar en la pantalla?> <form name="alta" action="<?php echo $this->baseUrl() ?>/world/guardar" method="post"> <table> <tr> <td>ID</td> <td><input type="text" name="ID" value="<?php echo $city->ID ?>"></td> </tr> <tr> <td>Name</td> <td><input type="text" name="Name" value="<?php echo $city->Name ?>"></td> </tr> <tr> <td>CountryCode</td> <td><input type="text" name="CountryCode" value="<?php echo $city->CountryCode ?>"></td> </tr> <tr> <td>District</td> <td><input type="text" name="District" value="<?php echo $city->District ?>"></td> </tr> <tr> <td>Population</td> <td><input type="text" name="Population" value="<?php echo $city->Population ?>"></td> </tr> <tr> <td colspan="2"><input type="submit" name="guardar" value="guardar"></td> </tr> </table> </form>
Allí se muestra un formulario con los datos cargados de la entrada a modificar, y el botón para confirmar los cambios.
El botón pasará por POST los datos del formulario, y le indicará al #/application/controllers/WorldController.php que ejecute la acción de guardar guardarAction(). Esta acción detectará el ID que llegó por POST (como se indicó anteriormente),
if($ID){
preparará los datos modificados,
$data = array( 'Name' => $this->_request->getParam('Name'), 'CountryCode' => $this->_request->getParam('CountryCode'), 'District' => $this->_request->getParam('District'), 'Population' => $this->_request->getParam('Population') );
y le indicará al modelo #/application/models/City.php que actualice los datos de la entrada correspondiente al ID indicado con los datos nuevos.
if($ID){ // Se realiza la actualización sobre la entrada seleccionada $where = $city->getAdapter()->quoteInto('ID = ?', $ID); $city->update($data, $where);
Luego la acción nos enviará a #/application/views/scripts/world/guardar.phtml nuevamente, en donde veremos un mensaje informando que la modificación fue exitosa, y nos da el enlace para volver al listado de ciudades.
Si queremos realizar una baja de alguna entrada, hacemos click en el enlace de 'baja' que esté en la misma fila que la entrada a eliminar.
Este enlace le pasará por GET el ID de la entrada que vamos a eliminar al #/application/controllers/WorldController.php, y éste ejecutará la acción de baja bajaAction().
// Para eliminar una entrada al hacer click en eliminar public function bajaAction() { $city = new City(); // Se obtiene la entrada a eliminar mediante el ID $where = $city->getAdapter()->quoteInto('ID = ?', $this->_request->getParam('ID')); // Se elimina la entrada de la Base de Datos $city->delete($where); }
Con el ID obtenido, la acción le dirá al modelo #/application/models/City.php que elimine la entrada correspondiente a dicho ID, y nos enviará a #/application/views/script/world/baja.phtml.
Entrada eliminada. <a href="<?php echo $this->baseUrl() ?>/world/index">listado</a>
Ésta nos mostrará un mensaje confirmando la baja de la entrada, y nos brindará un enlace para volver al listado de ciudades.
Estructura de archivos
editarLa estructura de los archivos será al siguiente
/.htaccess
editarEste archivo me permite realizar una redireccion utilizando apache, aqui me indica que cada vez que ingrese a la url http://localhost/zft ( que es donde reside este ejemplo ) nos redireccionara hacia http://localhost/zft/html que es la url base de la aplicacion.
RewriteEngine on RewriteRule !\.(js|ico|gif|jpg|png|css)$ html
/application/controllers/IndexController.php
editarEste es el controlador por defecto que se invoca en el caso de no especificar que controlador utilizar. Aqui podemos ver las acciones que el mismo puede realizar.
<?php class IndexController extends Zend_Controller_Action { function init() { $response = $this->getResponse(); $response->insert('sidebarLeft', $this->view->render('sidebarLeft.phtml')); $response->insert('sidebarRight', $this->view->render('sidebarRight.phtml')); $response->insert('header', $this->view->render('header.phtml')); $response->insert('footer', $this->view->render('footer.phtml')); } public function indexAction() { } }
/application/controllers/WorldController.php
editarAcá están especificadas las distintas acciones que se van a utilizar bajo la categoría 'world'.
<?php class WorldController extends Zend_Controller_Action { function init() { $response = $this->getResponse(); $response->insert('sidebarLeft', $this->view->render('sidebarLeft.phtml')); $response->insert('sidebarRight', $this->view->render('sidebarRight.phtml')); $response->insert('header', $this->view->render('header.phtml')); $response->insert('footer', $this->view->render('footer.phtml')); } // El index es por defecto el listado public function indexAction() { $city = new City(); $this->view->cities = $city->listCities(); } // Aqui solo es necesario el ingreso de datos public function altaAction() { } // Para eliminar una entrada al hacer click en eliminar public function bajaAction() { $city = new City(); // Se obtiene la entrada a eliminar mediante el ID $where = $city->getAdapter()->quoteInto('ID = ?', $this->_request->getParam('ID')); // Se elimina la entrada de la Base de Datos $city->delete($where); } // Para modificar los valores de una entrada al hacer click en modificar public function modificacionAction() { $city = new City(); // Se obtiene los datos de la entrada a modificar mediante el ID $where = $city->getAdapter()->quoteInto('ID = ?', $this->_request->getParam('ID')); $this->view->city = $city->fetchAll($where); } // public function guardarAction() { $city = new City(); // Si llega un ID por POST o GET, se guarda en esta variable. // Sino, se le asigna false (para determinar más adelante si se trata // de un alta o una modificación) $ID = $this->_request->getParam('ID',false); // Preparación de los datos para ser guardados $data = array( 'Name' => $this->_request->getParam('Name'), 'CountryCode' => $this->_request->getParam('CountryCode'), 'District' => $this->_request->getParam('District'), 'Population' => $this->_request->getParam('Population') ); // Si por POST o GET vino un ID, es porque se trata de una modificacion if($ID){ // Se realiza la actualizacion sobre la entrada seleccionada $where = $city->getAdapter()->quoteInto('ID = ?', $ID); $city->update($data, $where); // Sino, se trata de un alta }else{ // Se realiza el alta de una nueva entrada $city->insert($data); } } }
/application/views/layouts/layout.phtml
editarEsta pagina nos permite definir la estructura del sitio, es decir su distribucion topografica, como ser si dispone de un encabezado, pie de pagina, barra lateral izquierada, barra lateral derecha y donde residira el contenido de la accion que se esta ejecutando actualmente.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title>ZFTutorial :: Enfoque pragmatico</title> <link rel="stylesheet" type="text/css" href="<?php echo $this->baseUrl();?>/css/main.css"> <link rel="stylesheet" type="text/css" href="<?php echo $this->baseUrl();?>/css/grid.css"> </head> <body> <div id="bcss-header"> <!-- bcss-header --> <?php echo $this->layout()->header ?> <!-- /bcss-header --> </div> <div id="bcss-sidebar-1"> <!-- bcss-sidebar-1 --> <?php echo $this->layout()->sidebarLeft ?> <!-- /bcss-sidebar-1 --> </div> <div id="bcss-content"> <!-- bcss-content --> <?php echo $this->layout()->content ?> <!-- /bcss-content --> </div> <div id="bcss-sidebar-2"> <!-- bcss-sidebar-2 --> <?php echo $this->layout()->sidebarRight ?> <!-- /bcss-sidebar-2 --> </div> <div id="bcss-footer"> <!-- bcss-footer --> <?php echo $this->layout()->footer ?> <!-- /bcss-footer --> </div> </body> </html>
/application/views/helpers/BaseUrl.php
editarEste helper me permite determinar en forma absoluta la url base de la aplicación.
<?php class Zend_View_Helper_BaseUrl { function baseUrl() { $fc = Zend_Controller_Front::getInstance(); $request = $fc->getRequest(); return $request->getBaseUrl(); } }
/application/views/scripts/sidebarLeft.phtml
editarEste archivo forma parte del layout #/application/views/layouts/layout.phtml.
Esta sección incluye el contenido de la parte lateral izquierda del layout.
<ul> <li><a href="<?php echo $this->baseUrl();?>/index/index">home</a></li> <li><a href="<?php echo $this->baseUrl();?>/world/index">world</a></li> <li><a href="http://www.google.com">google</a></li> </ul>
/application/views/scripts/header.phtml
editarEste archivo forma parte del layout #/application/views/layouts/layout.phtml.
Esta sección incluye el contenido del encabezado del layout.
<h1>Header</h1>
/application/views/scripts/footer.phtml
editarEste archivo forma parte del layout #/application/views/layouts/layout.phtml.
Esta sección incluye el contenido del pie del layout.
<h1>Footer</h1>
/application/views/scripts/sidebarRight.phtml
editarEste archivo forma parte del layout #/application/views/layouts/layout.phtml.
Esta sección incluye el contenido de la parte lateral derecha del layout.
<h1>Sidebar Right</h1>
/application/views/scripts/index/index.phtml
editarEste archivo forma parte del layout #/application/views/layouts/layout.phtml.
Esta sección incluye el contenido de la accion que se esta ejecutando.
<h1>Hello World !!!</h1>
/application/views/scripts/world/alta.phtml
editarAcá se muestra el formulario a completar para dar un alta y agregar una entrada a la tabla de ciudades (City)
<form name="alta" action="<?php echo $this->baseUrl() ?>/world/guardar" method="post"> <table> <tr> <td>Name</td> <td><input type="text" name="Name" value=""></td> </tr> <tr> <td>CountryCode</td> <td><input type="text" name="CountryCode" value=""></td> </tr> <tr> <td>District</td> <td><input type="text" name="District" value=""></td> </tr> <tr> <td>Population</td> <td><input type="text" name="Population" value=""></td> </tr> <tr> <td colspan="2"><input type="submit" name="guardar" value="Guardar"></td> </tr> </table> </form>
/application/views/scripts/world/baja.phtml
editarEste mensaje se muestra luego de dar de baja una ciudad de la tabla de ciudades (City), y se ofrece un enlace para volver al listado de ciudades.
Entrada eliminada. <a href="<?php echo $this->baseUrl() ?>/world/index">listado</a>
/application/views/scripts/world/guardar.phtml
editarEste mensaje se muestra luego de agregar o modificar una entrada de la tabla de ciudades (City), y se ofrece un enlace para volver al listado de ciudades.
Alta/modificación realizada con éxito. <a href="<?php echo $this->baseUrl() ?>/world/index">Listado</a>
/application/views/scripts/world/index.phtml
editarAcá se muestra el listado de las últimas 10 ciudades con sus respectivos datos de la tabla de ciudades (previamente cargada en el WorldController) y los enlaces correspondientes para realizar un alta, modificaciones, o dar de baja a las entradas.
<table border="1"> <tr> <th>ID</th> <th>Name</th> <th>CountryCode</th> <th>District</th> <th>Population</th> <th><a href="<?php echo $this->baseUrl() ?>/world/alta">Alta</a></th> </tr> <?php foreach ($this->cities as $city) {?> <tr> <td><?=$city->ID?></td> <td><?=$city->Name?></td> <td><?=$city->CountryCode?></td> <td><?=$city->District?></td> <td><?=$city->Population?></td> <td> <a href="<?php echo $this->url(array('controller'=>'world','action'=>'modificacion','ID'=>$city->ID));?>">Editar</a> <a href="<?php echo $this->url(array('controller'=>'world','action'=>'baja','ID'=>$city->ID));?>">Borrar kirochi</a> </td> </tr> <?php } ?> </table>
/application/views/scripts/world/modificacion.phtml
editarAcá se muestra el formulario con los datos de la entrada a modificar, para poder realizar la actualización correspondiente.
<?php // Se guarda en esta variable los datos de la entrada a modificar $city = $this->city->current(); // Se cargan los datos de la entrada a modificar en la pantalla?> <form name="alta" action="<?php echo $this->baseUrl() ?>/world/guardar" method="post"> <table> <tr> <td>ID</td> <td><input type="text" name="ID" value="<?php echo $city->ID ?>"></td> </tr> <tr> <td>Name</td> <td><input type="text" name="Name" value="<?php echo $city->Name ?>"></td> </tr> <tr> <td>CountryCode</td> <td><input type="text" name="CountryCode" value="<?php echo $city->CountryCode ?>"></td> </tr> <tr> <td>District</td> <td><input type="text" name="District" value="<?php echo $city->District ?>"></td> </tr> <tr> <td>Population</td> <td><input type="text" name="Population" value="<?php echo $city->Population ?>"></td> </tr> <tr> <td colspan="2"><input type="submit" name="guardar" value="guardar"></td> </tr> </table> </form>
/application/models/City.php
editarEste es un modelo que se va a utilizar para cargar los datos de la tabla de cuidades y poder utilizarlos para las distintas pantallas y formularios.
<?php class City extends Zend_Db_Table_Abstract { protected $_name = 'City'; // Devuelve un listado de 10 ciudades ordenadas descendentemente por ID public function listCities(){ $result = $this->fetchAll(null ,'id desc',10,0); return $result; } }
/html/index.php
editarEste archivo es el boot de la aplicacion. Aqui seteamos los archivos de configuracion, acceso a datos, archivos de log, etc. Aqui se setea el frontController y el manejador de layout.
<?php /** * Zend Framework Tutorial * * Este tutorial tiene un enfoque pragmatico, lo cual indica una amplia cantidad * de ejemplos. Este material forma parte del Wikibook en español para ZF. * * @author Mario Garcia * @copyright Copyright (c) 2006-2008 Oh!Studio Media Solutions (http://www.ohstudio.com.ar) * @license http://www.php.net/license/3_0.txt */ <?php define('ROOT_DIR', dirname(dirname(__FILE__))); // Setup path to the Zend Framework files set_include_path('.' . PATH_SEPARATOR . ROOT_DIR.'/lib/' . PATH_SEPARATOR . ROOT_DIR.'/application/models/' . PATH_SEPARATOR . get_include_path() ); require_once 'Zend/Loader.php'; Zend_Loader::registerAutoload(); // Inicializar el MVC Zend_Layout::startMvc(array('layoutPath' => ROOT_DIR.'/application/views/layouts')); try { // Seteos para la conexion con la base de datos $db = Zend_Db::factory('Pdo_Mysql', array( 'host' => 'localhost', 'username' => 'root', 'password' => 'r00t', 'dbname' => 'world' )); //Test de conexion con la base de datos $db->getConnection(); // Establecemos que $db sera el Adapter por defecto Zend_Db_Table_Abstract::setDefaultAdapter($db); } catch (Zend_Db_Adapter_Exception $e) { //Sucedio un error con las credenciales del usuario o la base de datos. die($e->getMessage()); } catch (Zend_Exception $e) { // Sucedio un error inexperado die($e->getMessage()); } // Run! $frontController = Zend_Controller_Front::getInstance(); $frontController->addControllerDirectory(ROOT_DIR.'/application/controllers'); $frontController->throwExceptions(true); try { $frontController->dispatch(); } catch(Exception $e) { echo nl2br($e->__toString()); }
/html/.htaccess
editarEste archivo me permite determinar que todo archivo que no sea una imagen, ni js, ni css sea redireccionado hacia index.php.
RewriteEngine on RewriteRule !\.(js|ico|gif|jpg|png|css)$ index.php
/html/css/grid.css
editarEste archivo forma parte del layout #/application/views/layouts/layout.phtml.
Aquí le aplicamos el estilo para la distribución de las secciones.
/* Site Grid by BoxedCSS.com */ #bcss-header { width:100%; background:#FFFFE5; /* you can delete this, it's just a visual aid */ clear:both; } #bcss-sidebar-1 { width:27%; float:left; background:#FFE5FF; /* you can delete this, it's just a visual aid */ } #bcss-sidebar-2 { width:13%; float:left; background:#F7FBEA; /* you can delete this, it's just a visual aid */ } #bcss-content { width:60%; min-height: 400px; float:left; background:#E5F2FF; /* you can delete this, it's just a visual aid */ } #bcss-footer { width:100%; clear:both; background:#FFF2E5; /* you can delete this, it's just a visual aid */ }