Ir al contenido

Zend Framework/Zend Db/ABM Básico con Lógica de Negocio

De Wikilibros, la colección de libros de texto de contenido libre.

Introducción

[editar]

En este ejemplo está basado en el ejemplo Zend Framework/Zend Db/ABM Básico con Validaciones (se recomienda ver los ejemplos anteriores). Aquí veremos cómo aplicar una lógica de negocio al proyecto dividiendo ciertos procesos en dos capas: una de acceso a datos y otra de lógicas de negocio.

El controlador deberá usar a objetos de la capa de lógica de negocio, que accederán a objetos de la capa de acceso a datos, y esta última será la única habilitada para leer, modificar y eliminar datos de las tablas (en este ejemplo en particular, sólo de la tabla de ciudades).

En la próxima 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_ABM_LogicaDeNegocio.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.

Lógica de Negocio

[editar]

Capa de Acceso a Datos

[editar]

El acceso a la base de datos lo realizaremos mediante Data Access Objects (DAO). En este ejemplo, utilizaremos la clase DaoCity definida en #/application/models/DaoCity.php para acceder directamente a la tabla de ciudades. El prefijo Dao es una convención para indicar que es uno de estos objetos mencionados.

<?php
class DaoCity extends Zend_Db_Table_Abstract
{
	// Nombre de la tabla
	protected $_name = 'City';
	// Campo primario de la tabla
	protected $_primary = 'ID';
	
	// Devuelve un listado de $piFilas ciudades ordenadas descendentemente por ID
	// En este ejemplo todavía no está preparada la lógica generalizada de buscar por campo
	public function listar($piFilas){
		return $this->fetchAll(null ,'id desc',$piFilas,0);
	}
	
	// Devuelve los datos de una entrada con un ID determinado
	public function buscarPorID($ID) {
		return $this->fetchAll( $this->getAdapter()->quoteInto('ID = ?', $ID) );
	}
	
	
	// Llama al método guardar() pasando el ID como 'false' para indicarle que se trata de un alta
	public function crear($psName, $psCountryCode, $psDistrict, $piPopulation) {
		$this->guardar(false, $psName, $psCountryCode, $psDistrict, $piPopulation);
	}
	
	// Elimina una entrada determinada
	public function eliminar($ID) {
		// Se obtiene la entrada a eliminar mediante el ID
		$where = $this->getAdapter()->quoteInto('ID = ?', $ID);
		// Se elimina la entrada de la tabla
		$this->delete($where);
	}
	
	// Llama al método guardar() pasando el ID a modificar para indicarle que se trata de una modificación 
	public function modificar($ID, $psName, $psCountryCode, $psDistrict, $piPopulation) {
		$this->guardar($ID, $psName, $psCountryCode, $psDistrict, $piPopulation);
	}
	
	
	// Guarda o modifica entradas
	protected function guardar($ID = false, $psName, $psCountryCode, $psDistrict, $piPopulation) {
		// Preparación de los datos para ser guardados
		$data = array(
		    'Name'			=> $psName,
		    'CountryCode'	=> $psCountryCode,
			'District'		=> $psDistrict,
			'Population'	=> $piPopulation
		);
		// Si la función obtuvo un ID como parámetro:
		if($ID){
			// Se realiza la actualización sobre la entrada seleccionada
			$where = $this->getAdapter()->quoteInto('ID = ?', $ID);
			$this->update($data, $where);
		// Sino, se trata de un alta
		}else{
			// Se realiza el alta de una nueva entrada
			$this->insert($data);	
		}
	}
}

En la cabeza de la definición de esta clase podemos ver que hereda de la clase Zend_Db_Table_Abstract. Por esto es que definimos dos variables necesarias para que las cosas que hereda de Zend_Db_Table_Abstract funcionen correctamente.

class DaoCity extends Zend_Db_Table_Abstract
{
	// Nombre de la tabla
	protected $_name = 'City';
	// Campo primario de la tabla
	protected $_primary = 'ID';

La primera debe contenter el nombre de la tabla a la cuál vamos a acceder mediante esta clase. En este ejemplo, la tabla se llama City. Si necesitáramos acceder a más de una tabla en el proyecto, debemos crear una clase de acceso por cada tabla.

La segunda indica el campo primario de la tabla a la cuál accederá esta clase.

Luego podemos ver los métodos de la clase, que se encargan de administrar los datos de la tabla especificada.

	// Devuelve un listado de $piFilas ciudades ordenadas descendentemente por ID
	// En este ejemplo todavía no está preparada la lógica generalizada de buscar por campo
	public function listar($piFilas){
		return $this->fetchAll(null ,'id desc',$piFilas,0);
	}

Este método devuelve una lista de las últimas entradas en orden descendente por ID, y recibe como parámetro la cantidad de filas que se listarán.

Este método debería ser también una generalización de los métodos buscarPorXXXX(), tomando como parámetros el campo por el cuál se realizará la búsqueda (el cuál determinará el método buscarPorXXXX() a utilizar), y el valor buscado. Pero esta lógica todavía no está lista, y no se ha incluido ya que se pueden utilizar los métodos buscarPorXXXX() individualmente también.

	// Devuelve los datos de una entrada con un ID determinado
	public function buscarPorID($ID) {
		return $this->fetchAll( $this->getAdapter()->quoteInto('ID = ?', $ID) );
	}

Este método recupera todos los datos de una entrada seleccionándola por su ID, que es pasado como parámetro.

	// Llama al método guardar() pasando el ID como 'false' para indicarle que se trata de un alta
	public function crear($psName, $psCountryCode, $psDistrict, $piPopulation) {
		$this->guardar(false, $psName, $psCountryCode, $psDistrict, $piPopulation);
	}

Este método crea una nueva entrada en la tabla mediante los datos que se pasan como parámetros. Para ello llama al método guardar() de este mismo objeto, pasándole los datos, excepto el ID, para indicarle que se debe crear una nueva entrada.

	// Elimina una entrada determinada
	public function eliminar($ID) {
		// Se obtiene la entrada a eliminar mediante el ID
		$where = $this->getAdapter()->quoteInto('ID = ?', $ID);
		// Se elimina la entrada de la tabla
		$this->delete($where);
	}

Este método elimina una entrada que corresponde al ID pasado como parámetro.

	// Llama al método guardar() pasando el ID a modificar para indicarle que se trata de una modificación 
	public function modificar($ID, $psName, $psCountryCode, $psDistrict, $piPopulation) {
		$this->guardar($ID, $psName, $psCountryCode, $psDistrict, $piPopulation);
	}

Este método modifica los datos de una entrada existente, que la identificará con el ID pasado como parámetro. Para ello llama (al igual que el método crear()) al método guardar(), pero pasándole todos los datos, incluyendo el ID, para indicarle que se debe modificar los datos de esa entrada.

	// Guarda o modifica entradas
	protected function guardar($ID = false, $psName, $psCountryCode, $psDistrict, $piPopulation) {
		// Preparación de los datos para ser guardados
		$data = array(
		    'Name'			=> $psName,
		    'CountryCode'	=> $psCountryCode,
			'District'		=> $psDistrict,
			'Population'	=> $piPopulation
		);
		// Si la función obtuvo un ID como parámetro:
		if($ID){
			// Se realiza la actualización sobre la entrada seleccionada
			$where = $this->getAdapter()->quoteInto('ID = ?', $ID);
			$this->update($data, $where);
		// Sino, se trata de un alta
		}else{
			// Se realiza el alta de una nueva entrada
			$this->insert($data);	
		}
	}

Este método se encarga tanto de crear una entrada como de hacer una modificación. El mismo método discrimina de cuál de ambas situaciones se trata, dependiendo de si recibe un ID como parámetro o no. Como podemos ver, la lógica de este método es la misma que usamos en los ejemplos anteriores para guardar datos (Zend Framework/Zend Db/ABM Básico con Validaciones). En resumen: Recibe los datos necesarios como parámetros. Prepara los datos en un array. Si obtuvo un ID como parámetro, modifica la entrada correspondiente en la tabla. Si no obtuvo un ID, creo una nueva entrada con los datos en la tabla.


Hasta aquí, esta clase se encarga exclusivamente de consultar y editar la tabla de ciudades.

Capa de Lógica de Negocios

[editar]

En este ejemplo, esta capa es casi un mero "puente" entre el controlador #/application/controllers/WorldController.php y la capa de Acceso a Datos, ya que no hay mucha complejidad en el manejo de los datos que debemos mostrar. Sin embargo, la implementaremos igual porque sería muy importante si tuviéramos que procesar los datos de la base de datos (por ejemplo, obtener totales y subtotales de cierto campo, filtrar mediante condiciones no convencionales, hacer cálculos entre distintos campos y distintas entradas, etc) y por eso es muy importante en este ejemplo que intenta generalizar.

<?php
class BsCity
{
	// Devuelve un listado de las 10 últimas entradas.
	// En este ejemplo todavía no está preparada la lógica generalizada de buscar por campo
	public function listar() {
		$daoCity = new DaoCity();
		return $daoCity->listar(10);
	}
	
	// Devuelve los datos de una entrada dado su ID
	public function buscarPorID($ID) {
		$daoCity = new DaoCity();
		return $daoCity->buscarPorID($ID);
	}
	
	// Crea una entrada nueva en la tabla
	public function crear($psName, $psCountryCode, $psDistrict, $piPopulation) {
		$daoCity = new DaoCity();
		$daoCity->crear($psName, $psCountryCode, $psDistrict, $piPopulation);
	}

	// Elimina una entrada de un ID determinado
	public function eliminar($ID) {
		$daoCity = new DaoCity();
		$daoCity->eliminar($ID);
	}
	
	// Modifica una entrada existente dado su ID
	public function modificar($ID, $psName, $psCoutryCode, $psDistrict, $piPopulation) {
		$daoCity = new DaoCity();
		$daoCity->modificar($ID, $psName, $psCoutryCode, $psDistrict, $piPopulation);
	}
}
?>

Los métodos de esta clase cumplen cada uno la misma función que sus homólogos de la clase DaoCity de #/application/models/DaoCity.php, recibiendo y pasando los mismos parámetros. Un detalle que vale destacar es que el método listar() lo preparamos para que devuelva los datos de exactamente 10 entradas, pasándole este valor fijo al método homólogo de la clase DaoCity.

Para finalizar, en el controlador #/application/controllers/WorldController.php reemplazamos la lógica que usamos en ejemplos anteriores por los métodos de la nueva clase de negocios BsCity (#//application/models/BsCity.php) que pertenece a la capa de Lógica de Negocio.

<?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'));
        // Para poder crear, almacenar y manipular mensajes de errores entre las pantallas
        $this->_flashMessenger = $this->_helper->getHelper('FlashMessenger');
    }

    // El index es por defecto el listado
    public function indexAction()
    {
	    $bsCity = new BsCity();
	    // Prepara un listado de las 10 últimas ciudades
	    $this->view->cities = $bsCity->listar();   
    }
    
    // Aquí solo es necesario el ingreso de datos
    public function crearAction()
    {
    	// Prepara los mensajes de error
    	$this->view->mensajes = $this->_helper->_flashMessenger->getMessages();
    }
    
    // Para eliminar una entrada al hacer click en eliminar
    public function eliminarAction()
    {
		$bsCity = new BsCity();
		// Elimina la entrada con el ID especificado
		$bsCity->eliminar( $this->_request->getParam('ID') );
    }
    
    // Para modificar los valores de una entrada al hacer click en modificar
    public function modificarAction()
    {
		$bsCity = new BsCity();
		// Prepara los mensajes de error
		$this->view->mensajes = $this->_helper->_flashMessenger->getMessages();
		// Se obtiene los datos de la entrada a modificar mediante el ID
		$this->view->city = $bsCity->buscarPorID( $this->_request->getParam('ID') );
    }
    
    // Lógica para guardar datos, ya sea para dar un alta o para modificar
    public function guardarAction()
    {
		// 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);
		// Pasa el ID a la pantalla de guardar para saber si se trata de un alta
		// o una modificación
		$this->view->ID = $ID;
		
		// Preparación de los datos para la validación
		$lsName			= $this->_request->getParam('Name');
		$lsCountryCode	= $this->_request->getParam('CountryCode');
		$lsDistrict		= $this->_request->getParam('District');
		$liPopulation	= $this->_request->getParam('Population');
		
		// Objetos para las validaciones:
		// Para validar no vacíos
		$validator			= new Zend_Validate_NotEmpty();
		// Para validar números enteros
		$validatorInt		= new Zend_Validate_Int();
		// Para validar tres caracteres (para usar con el CountryCode)
		$validatorLength	= new Zend_Validate_StringLength(3, 3);
		
		// Contador de errores
		$iContador = 0; 
		
		// Si no se ingresó el nombre de la ciudad...
		if (!$validator->isValid($lsName)) {
			// ...genera un mensaje de error,...
			$this->_helper->_flashMessenger->addMessage('Debe ingresar un nombre.');
			// ...y suma uno al contador de errores.
			$iContador++;
		}
		// Si no se ingresó un código de país de tres caracteres...
		if (!$validatorLength->isValid($lsCountryCode)) {
			// ...genera un mensaje de error,...
			$this->_helper->_flashMessenger->addMessage('Debe ingresar un código de país de tres caracteres.');
			// ...y suma uno al contador de errores.
			$iContador++;
		}
		// Si no se ingresó un districto...
		if (!$validator->isValid($lsDistrict)) {
			// ...genera un mensaje de error,...
			$this->_helper->_flashMessenger->addMessage('Debe ingresar un districto.');
			// ...y suma uno al contador de errores.
			$iContador++;
		}
		// Si no se ingresó la cantidad de población...
		if (!$validator->isValid($liPopulation)) {
			// ...genera un mensaje de error,...
			$this->_helper->_flashMessenger->addMessage('Debe ingresar una cantidad de población.');
			// ...y suma uno al contador de errores.
			$iContador++;
		}
		// Si la cantidad de población no es un número...
		if (!$validatorInt->isValid($liPopulation)) {
			// ...genera un mensaje de error,...
			$this->_helper->_flashMessenger->addMessage('La cantidad de población debe ser un número.');
			// ...y suma uno al contador de errores.
			$iContador++;
		}
		
		// Si no hubo errores de validación:
		if ($iContador == 0) {
			$bsCity = new BsCity();
			// Guarda en la tabla los datos ingresados (y después muestra la pantalla 'guardar')
			if ($ID) {
				$bsCity->modificar($ID, $lsName, $lsCountryCode, $lsDistrict, $liPopulation);
			} else {
				$bsCity->crear($lsName, $lsCountryCode, $lsDistrict, $liPopulation);
			}
			//$bsCity->guardar($ID, $lsName, $lsCountryCode, $lsDistrict, $liPopulation);
		// Si hubo algún error de validación:
		} else {
			// Se crea un objeto destinado a redireccionar
			$this->_redirector = $this->_helper->getHelper('Redirector');
			// Si por POST o GET vino un ID, es porque se trata de una modificación, y sino, es un alta 
			if($ID) {
				// Si era una modificación, vuelvo al formulario de modificación para mostrar los errores
				// de validación y le paso el ID de nuevo
				$this->_redirector->gotoUrl('/world/modificar/ID/' . $ID);
			} else {
				// Si era un alta, vuelvo al formulario de alta para mostrar los errores de validación
				// y para que se reingresen los datos
				$this->_redirector->gotoUrl('/world/crear');
			}
		}
    }
}

Como podemos ver, lo único que mantenemos en la lógica del controlador son las validaciones de ingresos de datos, y la carga de los mensajes de error. Todo lo que manejaba los datos de la tabla fue reemplazado y encapsulado por la nueva Lógica de Negocio.

Estructura de archivos

[editar]

La estructura de los archivos será al siguiente

Archivo:Zft db basica abm logica negocio.png

/html/index.php

[editar]

Breve descripción del archivo

Plantilla:Zf /html/index.php

<?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

[editar]

Breve descripción del archivo

Este 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

[editar]

Breve descripción del archivo

Este 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 */

	}


/application/models/BsCity.php

[editar]

Breve descripción del archivo

Plantilla:Zf /application/models/BsCity.php

<?php
class BsCity
{
	// Devuelve un listado de las 10 últimas entradas.
	// En este ejemplo todavía no está preparada la lógica generalizada de buscar por campo
	public function listar() {
		$daoCity = new DaoCity();
		return $daoCity->listar(10);
	}
	
	// Devuelve los datos de una entrada dado su ID
	public function buscarPorID($ID) {
		$daoCity = new DaoCity();
		return $daoCity->buscarPorID($ID);
	}
	
	// Crea una entrada nueva en la tabla
	public function crear($psName, $psCountryCode, $psDistrict, $piPopulation) {
		$daoCity = new DaoCity();
		$daoCity->crear($psName, $psCountryCode, $psDistrict, $piPopulation);
	}

	// Elimina una entrada de un ID determinado
	public function eliminar($ID) {
		$daoCity = new DaoCity();
		$daoCity->eliminar($ID);
	}
	
	// Modifica una entrada existente dado su ID
	public function modificar($ID, $psName, $psCoutryCode, $psDistrict, $piPopulation) {
		$daoCity = new DaoCity();
		$daoCity->modificar($ID, $psName, $psCoutryCode, $psDistrict, $piPopulation);
	}
}
?>


/application/models/DaoCity.php

[editar]

Breve descripción del archivo

Plantilla:Zf /application/models/DaoCity.php

<?php
class DaoCity extends Zend_Db_Table_Abstract
{
	// Nombre de la tabla
	protected $_name = 'City';
	// Campo primario de la tabla
	protected $_primary = 'ID';
	
	// Devuelve un listado de $piFilas ciudades ordenadas descendentemente por ID
	// En este ejemplo todavía no está preparada la lógica generalizada de buscar por campo
	public function listar($piFilas){
		return $this->fetchAll(null ,'id desc',$piFilas,0);
	}
	
	// Devuelve los datos de una entrada con un ID determinado
	public function buscarPorID($ID) {
		return $this->fetchAll( $this->getAdapter()->quoteInto('ID = ?', $ID) );
	}
	
	
	// Llama al método guardar() pasando el ID como 'false' para indicarle que se trata de un alta
	public function crear($psName, $psCountryCode, $psDistrict, $piPopulation) {
		$this->guardar(false, $psName, $psCountryCode, $psDistrict, $piPopulation);
	}
	
	// Elimina una entrada determinada
	public function eliminar($ID) {
		// Se obtiene la entrada a eliminar mediante el ID
		$where = $this->getAdapter()->quoteInto('ID = ?', $ID);
		// Se elimina la entrada de la tabla
		$this->delete($where);
	}
	
	// Llama al método guardar() pasando el ID a modificar para indicarle que se trata de una modificación 
	public function modificar($ID, $psName, $psCountryCode, $psDistrict, $piPopulation) {
		$this->guardar($ID, $psName, $psCountryCode, $psDistrict, $piPopulation);
	}
	
	
	// Guarda o modifica entradas
	protected function guardar($ID = false, $psName, $psCountryCode, $psDistrict, $piPopulation) {
		// Preparación de los datos para ser guardados
		$data = array(
		    'Name'			=> $psName,
		    'CountryCode'	=> $psCountryCode,
			'District'		=> $psDistrict,
			'Population'	=> $piPopulation
		);
		// Si la función obtuvo un ID como parámetro:
		if($ID){
			// Se realiza la actualizacion sobre la entrada seleccionada
			$where = $this->getAdapter()->quoteInto('ID = ?', $ID);
			$this->update($data, $where);
		// Sino, se trata de un alta
		}else{
			// Se realiza el alta de una nueva entrada
			$this->insert($data);	
		}
	}
}


/application/controllers/WorldController.php

[editar]

Breve descripción del archivo

Plantilla:Zf /application/controllers/WorldController.php

<?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'));
        // Para poder crear, almacenar y manipular mensajes de errores entre las pantallas
        $this->_flashMessenger = $this->_helper->getHelper('FlashMessenger');
    }

    // El index es por defecto el listado
    public function indexAction()
    {
	    $bsCity = new BsCity();
	    // Prepara un listado de las 10 últimas ciudades
	    $this->view->cities = $bsCity->listar();   
    }
    
    // Aquí solo es necesario el ingreso de datos
    public function crearAction()
    {
    	// Prepara los mensajes de error
    	$this->view->mensajes = $this->_helper->_flashMessenger->getMessages();
    }
    
    // Para eliminar una entrada al hacer click en eliminar
    public function eliminarAction()
    {
		$bsCity = new BsCity();
		// Elimina la entrada con el ID especificado
		$bsCity->eliminar( $this->_request->getParam('ID') );
    }
    
    // Para modificar los valores de una entrada al hacer click en modificar
    public function modificarAction()
    {
		$bsCity = new BsCity();
		// Prepara los mensajes de error
		$this->view->mensajes = $this->_helper->_flashMessenger->getMessages();
		// Se obtiene los datos de la entrada a modificar mediante el ID
		$this->view->city = $bsCity->buscarPorID( $this->_request->getParam('ID') );
    }
    
    // Lógica para guardar datos, ya sea para dar un alta o para modificar
    public function guardarAction()
    {
		// 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);
		// Pasa el ID a la pantalla de guardar para saber si se trata de un alta
		// o una modificación
		$this->view->ID = $ID;
		
		// Preparación de los datos para la validación
		$lsName			= $this->_request->getParam('Name');
		$lsCountryCode	= $this->_request->getParam('CountryCode');
		$lsDistrict		= $this->_request->getParam('District');
		$liPopulation	= $this->_request->getParam('Population');
		
		// Objetos para las validaciones:
		// Para validar no vacíos
		$validator			= new Zend_Validate_NotEmpty();
		// Para validar números enteros
		$validatorInt		= new Zend_Validate_Int();
		// Para validar tres caracteres (para usar con el CountryCode)
		$validatorLength	= new Zend_Validate_StringLength(3, 3);
		
		// Contador de errores
		$iContador = 0; 
		
		// Si no se ingresó el nombre de la ciudad...
		if (!$validator->isValid($lsName)) {
			// ...genera un mensaje de error,...
			$this->_helper->_flashMessenger->addMessage('Debe ingresar un nombre.');
			// ...y suma uno al contador de errores.
			$iContador++;
		}
		// Si no se ingresó un código de país de tres caracteres...
		if (!$validatorLength->isValid($lsCountryCode)) {
			// ...genera un mensaje de error,...
			$this->_helper->_flashMessenger->addMessage('Debe ingresar un código de país de tres caracteres.');
			// ...y suma uno al contador de errores.
			$iContador++;
		}
		// Si no se ingresó un districto...
		if (!$validator->isValid($lsDistrict)) {
			// ...genera un mensaje de error,...
			$this->_helper->_flashMessenger->addMessage('Debe ingresar un districto.');
			// ...y suma uno al contador de errores.
			$iContador++;
		}
		// Si no se ingresó la cantidad de población...
		if (!$validator->isValid($liPopulation)) {
			// ...genera un mensaje de error,...
			$this->_helper->_flashMessenger->addMessage('Debe ingresar una cantidad de población.');
			// ...y suma uno al contador de errores.
			$iContador++;
		}
		// Si la cantidad de población no es un número...
		if (!$validatorInt->isValid($liPopulation)) {
			// ...genera un mensaje de error,...
			$this->_helper->_flashMessenger->addMessage('La cantidad de población debe ser un número.');
			// ...y suma uno al contador de errores.
			$iContador++;
		}
		
		// Si no hubo errores de validación:
		if ($iContador == 0) {
			$bsCity = new BsCity();
			// Guarda en la tabla los datos ingresados (y después muestra la pantalla 'guardar')
			if ($ID) {
				$bsCity->modificar($ID, $lsName, $lsCountryCode, $lsDistrict, $liPopulation);
			} else {
				$bsCity->crear($lsName, $lsCountryCode, $lsDistrict, $liPopulation);
			}
			//$bsCity->guardar($ID, $lsName, $lsCountryCode, $lsDistrict, $liPopulation);
		// Si hubo algún error de validación:
		} else {
			// Se crea un objeto destinado a redireccionar
			$this->_redirector = $this->_helper->getHelper('Redirector');
			// Si por POST o GET vino un ID, es porque se trata de una modificación, y sino, es un alta 
			if($ID) {
				// Si era una modificación, vuelvo al formulario de modificación para mostrar los errores
				// de validación y le paso el ID de nuevo
				$this->_redirector->gotoUrl('/world/modificar/ID/' . $ID);
			} else {
				// Si era un alta, vuelvo al formulario de alta para mostrar los errores de validación
				// y para que se reingresen los datos
				$this->_redirector->gotoUrl('/world/crear');
			}
		}
    }
}


/application/controllers/IndexController.php

[editar]

Breve descripción del archivo

Este 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/views/scripts/sidebarRight.phtml

[editar]

Breve descripción del archivo

Este 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/footer.phtml

[editar]

Breve descripción del archivo

Este 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/header.phtml

[editar]

Breve descripción del archivo

Este 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/sidebarLeft.phtml

[editar]

Breve descripción del archivo

Este 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/world/index.phtml

[editar]

Breve descripción del archivo

Plantilla:Zf /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/crear">Crear</a></th>

  </tr>


<?php foreach ($this->cities as $city) {?>
//correccion kirochi, CHILE
<tr>
     <td><?php echo $city->ID?></td>
    <td><?php echo $city->Name?></td>
    <td><?php echo $city->CountryCode?></td>
    <td><?php echo $city->District?></td>
    <td><?php echo $city->Population?></td>
    <td>
	    <a href="<?php echo $this->baseUrl() ?>/world/modificar/ID/<?=$city->ID?>">Modificar</a>
	    <a href="<?php echo $this->baseUrl() ?>/world/eliminar/ID/<?=$city->ID?>">Eliminar</a>
    </td>
  </tr>
  
<?php }?>
</table>

/application/views/scripts/world/crear.phtml

[editar]

Breve descripción del archivo

Plantilla:Zf /application/views/scripts/world/crear.phtml

<span>
<?php
// Si hay mensajes de error provenientes de una validación, los muestra
if (count($this->mensajes) > 0) {
	foreach ($this->mensajes as $msg) {
		echo $msg.'<br>';
	}
}
?>
</span>
<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/eliminar.phtml

[editar]

Breve descripción del archivo

Plantilla:Zf /application/views/scripts/world/eliminar.phtml

Entrada eliminada.

<a href="<?php echo $this->baseUrl() ?>/world/index">listado</a>


/application/views/scripts/world/modificar.phtml

[editar]

Breve descripción del archivo

Plantilla:Zf /application/views/scripts/world/modificar.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?>
<span>
<?php
// Si hay mensajes de error provenientes de una validación, los muestra
if (count($this->mensajes) > 0) {
	foreach ($this->mensajes as $msg) {
		echo $msg.'<br>';
	}
}
?>
</span>
<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 ?>" readonly="readonly">
    </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/views/scripts/world/guardar.phtml

[editar]

Breve descripción del archivo

Plantilla:Zf /application/views/scripts/world/guardar.phtml

<?php
// Muestro un mensaje de éxito en la operación dependiendo si es un alta o
// una modificación
if ($this->ID) {
	echo "Modificación";
} else {
	echo "Alta";
}
?> realizada con éxito.

<a href="<?php echo $this->baseUrl() ?>/world/index">Listado</a>


/application/views/scripts/index/index.phtml

[editar]

Breve descripción del archivo

Este 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/layouts/layout.phtml

[editar]

Breve descripción del archivo

Esta 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

[editar]

Breve descripción del archivo

Este 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();
    }
}