martes, 18 de febrero de 2014

Las bases de datos

Ya empieza a tener forma nuestra aplicación, pero falta lo más importante, dotarla de la funcionalidad desead: la lógica de negocio. Como comenté anteriormente la filosofía de Zend Framework se basa en la arquitectura Modelo-Vista-Controlador, siendo el modelo las clases que representan las relaciones de nuestros datos.
Para empezar definiremos los parámetros de conexión en nuestro fichero application/configs/application.ini, en la sección [production] de la siguiente forma:
resources.db.adapter = PDO_MYSQL
resources.db.params.host = localhost
resources.db.params.username = nombre_usuario
resources.db.params.password = password
resources.db.params.dbname = nombre_base_de_datos

Evidentemente con los datos de seguridad adecuados. Con nuestro gestor de datos habitual crearemos la base de datos y el usuario, yo personalmente uso phpMyAdmin. Puede descargarse el paquete para Zend Server o directamente el código he instalarlo en nuestro servidor. Como ayuda la configuración del alias de Apache podría tener esta forma:

   Alias /phpmyadmin "C:\Zend\Apache2\htdocs\phpMyAdmin"
   <Directory "C:\Zend\Apache2\htdocs\phpMyAdmin">
       Options Indexes MultiViews FollowSymLinks
       AllowOverride All
       Order allow,deny
       Allow from all
   </Directory>

Para invocarlo tan solo es necesario navegar a la dirección http://localhost/phpmyadmin.
Al abordar el modelo de datos apreciamos la potencia de Zend Framework, dado que trabajaremos con formularios para introducir/editar la información, y por supuesto las vistas que serán las diferentes representaciones de nuestro modelo de datos.

El modelo de relaciones entre la tabla menú y menu_items es:



Las tablas de menús (menus y menu_items). Con phpMyAdmin crearemos las dos tablas:

--
-- Estructura de tabla para la tabla `menus`
--

CREATE TABLE IF NOT EXISTS `menus` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(50) DEFAULT NULL,
  `access_level` varchar(50) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;

--
-- Estructura de tabla para la tabla `menu_items`
--

CREATE TABLE IF NOT EXISTS `menu_items` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `menu_id` int(11) DEFAULT NULL,
  `label` varchar(250) DEFAULT NULL,
  `link` varchar(250) DEFAULT NULL,
  `position` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;

Para poder interactuar con estas tablas crearemos un modelo para cada una de ellas en nuestra aplicación. Una opción es crear un modelo por cada entidad, para más información se puede consultar la documentacion de Zend Framework Create a Model and Database Table.

Para indicarle a nuestra aplicación que busque los modelos de datos modificaremos nuestra clase Bootstrap para que los incluya.
Añadiremos la siguiente función miembro a la clase Bootstrap del fichero application/Bootstrap.php:
    protected function _initAutoload()
    {
     // Incluiremos un namespace vacio
     $autoLoader = Zend_Loader_Autoloader::getInstance();
     $autoLoader->registerNamespace('CMS_');
     $resourceLoader = new Zend_Loader_Autoloader_Resource(array(
       'basePath' => APPLICATION_PATH,
       'namespace' => '',
       'resourceTypes' => array(
               'model' => array(
                 'path' => 'models/',
                 'namespace' => 'Model_'
               ),
       ),
     ));
     // Devolvemos el autoloader que sera almacenado por el Bootstrap
     return $autoLoader;
    }

Como podemos observar nuestro modelo ha de tener la forma "Model_[nombre tabla]", y debe estar contenido en el directorio models. Las clases modelo derivan de la clase Zend_Db_Table_Abstract, que es la que nos proporcionará todas las funciones primitivas de acceso a los datos como selects, updates, deletes, etc. Es importante definir la variable protegida $_name que nos indica el nombre del modelo, asimismo en el modelo vendrán definidas las relaciones y las dependencias con el resto de modelos, es importante dado que podemos declarar acciones para garantizar la integridad de los datos. La variable que define la dependencia de tablas es un array $_dependentTables, y la de referencias otro de nombre $_referenceMap. Así nuestra clase Menú (en el fichero application/models/Menu.php) tendrá la siguiente forma:

<?php
require_once ('Zend/Db/Table/Abstract.php');

class Model_Menu extends Zend_Db_Table_Abstract
{

    protected $_name = 'menus';

    protected $_dependentTables = array(
            'Model_MenuItem'
    );
}

?>   
Y nuestra clase menu_items (en el fichero application/models/MenuItem.php) será:
<?php
require_once ('Zend/Db/Table/Abstract.php');

class Model_MenuItem extends Zend_Db_Table_Abstract
{

    protected $_name = 'menu_items';

    protected $_referenceMap = array(
            'Menu' => array(
                    'columns' => array(
                            'menu_id'
                    ),
                    'refTableClass' => 'Model_Menu',
                    'refColumns' => array(
                            'id'
                    ),
                    'onDelete' => self::CASCADE,
                    'onUpdate' => self::RESTRICT
            )
    );
}

?>

Como podemos ver en menu_items está definida la relación entre la clase menu_item (menu_id) y la clase menu (id), el detalle de la definición de las acciones 'onDelete' y 'onUpdate' para salvaguardar la integridad de los datos.

viernes, 14 de febrero de 2014

¿Cómo crear un portal multidiioma con Zend Framework?

Siguiendo con el tema de la codificación Zend Framework nos proporciona herramientas para crear un portal multiidioma.
La clase Zend_Translate dota a nuestros proyectos de forma nativa del soporte multiidioma, es decir, que componentes como Zend_View o Zend_Form son compatibles con esta internacionalización.
El primer paso a dar es determinar que formato tendrán nuestros diccionarios. La clase Zend_Adapter es la encargada de alimentar a Zend_Translate de los términos traducidos. Zend_Translate_Adapter es similar a los componentes de acceso a bases de datos como Zend_Db_Table y ofrecen diversos formatos de entrada:

  • Array: Codificación de los mensajes en código PHP dentro de nuestro proyecto.
  • CSV: texto separado por comas.
  • GetText: Ficheros de traducción *.mo.
  • Ini: Ficheros tradicionales *.ini.
Para más información sobre los diferentes formatos de entrada consultar la documentación de Zend_Translate_Adapters.
En nuestro ejemplo usaremos GetText, para ello es muy recomendable el uso del programa poedit para la generación de los diccionarios.

Para empezar crearemos una carpeta llamada languages en la raíz de application la cual será el destino del repositorio de diccionarios. Dentro crearemos dos ficheros (para empezar) para los idiomas inglés y castellano llamados dict-en.po y dict-es.po.

Contenido del fichero application/languages/dict-en.po:

"Project-Id-Version: CDocCMS"
"POT-Creation-Date:"
"PO-Revision-Date:"
"Last-Translator: Carles <carlesuc@gmail.com>"
"Language-Team:"
"MIME-Version: 1.0"
"Content-Type: text/plain; charset=iso-8859-1"
"Content-Transfer-Encoding: 8bit"
"Language: en"
"X-Generator: Poedit 1.6.4"

#: Diccionario Inglés
msgid "Powered by the Zend Framework"
msgstr "Powered by the Zend Framework"

Contenido del fichero application/languages/dict-es.po:
"Project-Id-Version: CDocCMS"
"POT-Creation-Date:"
"PO-Revision-Date:"
"Last-Translator: Carles <carlesuc@gmail.com>"
"Language-Team:"
"MIME-Version: 1.0"
"Content-Type: text/plain; charset=iso-8859-1"
"Content-Transfer-Encoding: 8bit"
"Language: es"
"X-Generator: Poedit 1.6.4"

#: Diccionario Castellano
msgid "Powered by the Zend Framework"
msgstr "Programado en Zend Framework"

Con la utilidad poedit compilaremos dichos ficheros para generar nuestros diccionarios dict-en.mo y dict-es.mo.
Ahora incluiremos los diccionarios en nuestro fichero application.ini para que sean incluidos como recursos.
Incluiremos las siguientes líneas en el fichero application/configs/application.ini:

resources.translate.adapter = Gettext
resources.translate.default.locale = "es_ES"
resources.translate.default.file = APPLICATION_PATH "/languages/dict-es.mo"
resources.translate.translation.en = APPLICATION_PATH "/languages/dict-en.mo"
resources.translate.translation.es = APPLICATION_PATH "/languages/dict-es.mo"

El siguiente paso es cargar el recurso de traducción en nuestro proyecto. Para ello crearemos un directorio donde ubicaremos nuestros recursos con la siguiente estructura:

/ library
  / CMS
    / Application
      / Resource

Crearemos un fichero de traducción llamado Translate.php. En este fichero crearemos una clase llamada CMS_Aplication_Resource_Translate que deriva de Zend_Application_Resource_ResourceAbstract.
El sistema de traducción necesita registrar la clase Zend_Translate, la cual previamente hemos de informar de que idiomas serán los soportados por nuestra aplicación. Para ello del fichero de configuración iremos iterando por los diferentes elementos resources.translate.translation.xx que existan, y los iremos incluyendo a la clase Zend_Translate.

El contenido del fichero library/CMS/Application/Resource/Translate.php es:
<?php
class CMS_Application_Resource_Translate extends Zend_Application_Resource_ResourceAbstract
{
 public function init ()
 {
  $options = $this->getOptions();
  $adapter = $options['adapter'];
  // Diccionario por defecto
  $defaultTranslation = $options['default']['file'];
  // Idioma por defecto
  $defaultLocale = $options['default']['locale'];
  $translate = new Zend_Translate($adapter, $defaultTranslation, $defaultLocale);
  // Iteramos entre los diferentes diccionarios y los vamos incluyendo
  foreach ($options['translation'] as $locale => $translation) {
   $translate->addTranslation($translation, $locale);
  }
  Zend_Registry::set('Zend_Translate', $translate);
  return $translate;
 }
}

Es necesario incluir en nuestro fichero de configuración la carga de los recuros de traducción, para ello debemos incluir la siguiente línea en el fichero application/configs/application.ini:

pluginPaths.CMS_Application_Resource = APPLICATION_PATH "/../library/CMS/Application/Resource"

Para ver el efecto de la traducción editamos nuestro fichero layout.phtml e incluimos la sentencia $this->translate.
Contenido del fichero application/layouts/scripts/layout.phtml:

<?php
echo $this->doctype ();
?>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
<?php
$this->loadSkin ( $this->skin );
echo $this->headMeta ();
echo $this->headTitle ();
echo $this->headScript ();
echo $this->headLink ();
?>
</head>
<body>
 <div id="container">
  <div id="header">
   <h1>Zend Framework CMS</h1>
  </div>
  <div id="navigation">
  <?php echo $this->layout()->nav;?>
 </div>
  <div id="content-container">
   <div id="content">
   <?php echo $this->layout()->content?>
  </div>
   <div id="aside">
   <?php echo $this->layout()->subNav;?> 
  </div>
   <div id="footer">
    <em><?php echo $this->translate('Powered by the Zend Framework');?></em>
   </div>
  </div>
 </div>
</body>
</html>

Podemos ver el resultado navegando por nuestro http://localhost:


Es cuestión de ir traduciendo los mensajes a nuestro gusto, como por ejemplo el título de la cabecera.

Estableciendo la codificación de nuestro portal

Los no anglo-parlantes nos vemos siempre en la problemática de los acentos, la codificación UTF-8 no los admite, se ha de hacer una conversión previa a esa codificación.
El problema es aún más grave si, como veremos, al usar diccionarios de traducción cuando se crean formularios si los campos tienen acentos veremos que no aparecen correctamente, y eso es debido a la función gettext que espera un tipo de codificación y encuentra caracteres que no son válidos.

Recordemos nuestra clase Bootstrap la cual dijimos que nos permitiría definir parámetros de configuración tanto de entorno como de componentes en concreto como nuestras vistas. Así si incluimos la codificación en esta clase, en la función encargada de la creación de las vistas (_initView), el problema queda solucionado.

Contenido del fichero application/Bootstrap.php:
<?php

class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
 protected function _initView()
 {
  // Initialize view
  $view = new Zend_View();
  $view->doctype('XHTML1_STRICT');
  $view->headTitle('Centro de Documentación');
  $view->skin = 'liquid';
  // Add it to the ViewRenderer
  $viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper(
    'ViewRenderer'
  );
  $viewRenderer->setView($view);
  // Return it, so that it can be stored by the bootstrap
  $view->setEncoding('ISO-8859-1');
  return $view;
 }
}

Ahora sí que podremos ver nuestro título con sus acentos.
Otra forma totalmente válida es incluir estos parámetros en nuestro application.ini, podría quedar más elegante, es una forma de centralizar los parámetros de configuración en un mismo lugar y desvincularlos del código, para que no tengamos que recorrer todo nuestro código buscando allí donde definimos tal parámetro.
Añadimos en el fichero application/configs/application.ini en la sección production las siguientes líneas:

resources.view.encoding = "ISO-8859-1"
resources.view.doctype = "XHTML1_STRICT"
resources.view.contentType = "text/html;charset=ISO-8859-1"
resources.view.Content-Language = "es_ES"
resources.view.headTitle = "Centro de documentación"

Ahora toca modificar nuestra clase Bootstrap para que lea estos parámetros:
Contenido del fichero application/Bootstrap.php:

<?php
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
 protected function _initView()
 {
 // Initialize view     
     $options = $this->getOptions();
     if (isset($options['resources']['view'])){
         $view = new Zend_View($options['resources']['view']);
     }
     else {
         $view = new Zend_View;
     }
     if (isset($options['resources']['view']['doctype'])) {
         $view->doctype($options['resources']['view']['doctype']);
     }
     if (isset($options['resources']['view']['contentType'])) {
         $view->headMeta()->appendHttpEquiv('Content-Type',$options['resources']['view']['contentType']);
     }
     if (isset($options['resources']['view']['Content-Language'])) {
      $view->headMeta()->appendHttpEquiv('Content-Language',$options['resources']['view']['Content-Language']);
     }
     if (isset($options['resources']['view']['headTitle'])) {
      $view->headTitle($options['resources']['view']['headTitle']);
     }
     $view->skin = 'liquid';
     // Add it to the ViewRenderer
     $viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper(
       'ViewRenderer'
     );
     $viewRenderer->setView($view);
     // Return it, so that it can be stored by the bootstrap
     return $view;
 }
}

Podemos ver que la instrucción setEncoding ha desaparecido, deja de tener sentido dado que le pasamos al constructor de la vista la codificación.
Al probar de nuevo la página no hemos de apreciar ningún cambio, podemos verificar que deja de funcionar la codificación ISO-8859-1 si eliminamos la línea resources.view.encoding del fichero applicaction.ini.
Sólo nos queda ir al fichero layout.phtml y elminar la línea que especifica la codificación, pues si vemos el código fuente HTML de la página resultante veremos que aparece repetido.

Contenido del fichero application/layouts/scripts/layout.phtml:

<?php
echo $this->doctype ();
?>
<html>
<head>
<?php
$this->loadSkin ( $this->skin );
echo $this->headMeta ();
echo $this->headTitle ();
echo $this->headScript ();
echo $this->headLink ();
?>
</head>

Ya que estamos y para finalizar la configuración del entorno podemos incluir el skin utilizado en nuestro fichoer application.ini de la misma forma que hemos hecho con la codificación.
Añadimos en el fichero application/configs/application.ini en la sección production la siguiente línea:

resources.view.skin = "liquid"
Ahora solo es necesario cargar en skine en la clase Bootstrap.

Contenido final de la clase Bootstrap en el archivo application/Bootstrap.php:

<?php
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
 protected function _initView()
 {
 // Initialize view     
     $options = $this->getOptions();
     if (isset($options['resources']['view']))
         $view = new Zend_View($options['resources']['view']);
     else
         $view = new Zend_View;
     if (isset($options['resources']['view']['doctype'])) 
         $view->doctype($options['resources']['view']['doctype']);
     if (isset($options['resources']['view']['contentType']))
         $view->headMeta()->appendHttpEquiv('Content-Type',$options['resources']['view']['contentType']);
     if (isset($options['resources']['view']['Content-Language'])) 
      $view->headMeta()->appendHttpEquiv('Content-Language',$options['resources']['view']['Content-Language']);
     if (isset($options['resources']['view']['headTitle'])) 
      $view->headTitle($options['resources']['view']['headTitle']);
     if (isset($options['resources']['view']['skin']))
      $view->skin = $options['resources']['view']['skin'];
     else       
      $view->skin = 'liquid';
     // Add it to the ViewRenderer
     $viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper(
       'ViewRenderer'
     );
     $viewRenderer->setView($view);
     // Return it, so that it can be stored by the bootstrap
     return $view;
 }
}

martes, 11 de febrero de 2014

Estableciendo skins para nuestro portal

Como he comentado anteriormente la utilización de plantillas (skins) para nuestro portal agiliza enormemente el desarrollo de nuestra aplicación, dado que focalizamos nuestros esfuerzos en el ámbito de la programación o en el del diseño del portal.

Vamos a dotar a nuestro portal de soporte para plantillas (skins), para ello crearemos en la carpeta public la subcarpeta skins. Dentro de skins la carpeta con nuestra plantilla, en nuestro caso la llamaremos liquid.  Dentro de la misma las subcarpetas css e images.
El árbol de directorios queda:
/ public
  / skins
    / liquid
      / css
      / images

Es momento de mover el fichero antes creado public/css/layout.css a public/skins/liquid/css/layout.css, como todas las imágenes que pudiéramos tener de public/images a public/skins/liquid/images.
Crearemos un fichero xml en la raíz de nuestro skin con todos las hojas de estilo necesarias. Así pues creamos el fichero public/skins/liquid/skin.xml.

Contenido de public/skins/liquid/skin.xml:
<?xml version="1.0" encoding="ISO-8859-1"?>
<skin>
  <stylesheets>
    <stylesheet>layout.css</stylesheet>
    <stylesheet>text.css</stylesheet>
  </stylesheets>
</skin>

Crearemos una hoja de estilo para los textos, al igual que layout.css la ubicaremos en public/skins/liquid/css y tendrá como nombre text.css.

@CHARSET "ISO-8859-1";
body {
  font-family:Helvetica, Arial, sans-serif;
}

Nota: Ahora la referencia de nuestras imágenes debe ser relativa al path del skin, por lo tanto no olvidar renombrar la única imagen que tenemos en nuestro css:
Actualizar el fichero public/skins/liquid/css/layout.css:

#content-container {
 float: left;
 width: 100%;
 background: #FFF url(/skins/liquid/images/layout-two-liquid-background.gif) repeat-y 68% 0;
}

En Zend Framework surge la necesidad de la reutilización del código, para ayudarnos Zend Framework pone a nuestra disposición las "View Helpers", que son clases que extienden de Zend_View_Helper_Abstract y nos permiten invocar a los métodos de estas clases desde diferentes lugares de la aplicación.
Las "View Helper" van ubicadas en el directorio application/views/helpers, allí crearemos nuestra "View Helper" llamada LoadSkin.php.

Contenido de application/views/helpers/LoadSkin.php:
<?php
/**
 * Este "helper" cargará las hojas de estilos de nuestro skin
 *
 */
class Zend_View_Helper_LoadSkin extends Zend_View_Helper_Abstract
{
 public function loadSkin ($skin)
 {
  // Leemos del fichero de configuración xml las hojas he iteramos entre ellas
  $skinData = new Zend_Config_Xml('./skins/' . $skin . '/skin.xml');  
  $stylesheets = $skinData->stylesheets->stylesheet;
  // La documentación de Zend_Config_Xml indica que los datos leídos se retornarán
  // como strings, en el caso de haber un solo elemento retornaría un string. 
  // http://framework.zend.com/manual/1.12/en/zend.config.adapters.xml.html
  if($stylesheets instanceof Zend_Config)
   $stylesheetsArray = $stylesheets->toArray();
  else
   $stylesheetsArray = array($stylesheets);
  // Iteramos entre los diferentes elementos
  if (is_array($stylesheetsArray)) {
   foreach ($stylesheetsArray as $stylesheet) {
    $this->view->headLink()->appendStylesheet('/skins/' . $skin .
      '/css/' . $stylesheet);
   }
  }
 }
}

Es necesario pasarle al "View Helper" una plantilla (skin) válida, podemos usar muchos métodos pero de momento una forma rápida de inicializar el sistema de plantillas es establecer el entorno desde la clase Bootstrap, para ello añadiremos un método nuevo llamado _initview:
Contenido del fichero application/Bootstrap.php:
<?php
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
 protected function _initView()
 {
  // Initialize view
  $view = new Zend_View();
  $view->doctype('XHTML1_STRICT');
  $view->headTitle('Centro de Documentación');
  $view->skin = 'liquid';
  // Add it to the ViewRenderer
  $viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper(
    'ViewRenderer'
  );
  $viewRenderer->setView($view);
  // Return it, so that it can be stored by the bootstrap
  return $view;
 }
}

Ahora que ya hemos inicializado las vistas con nuestra plantilla, solo nos falta invocarla en el layout: Meta del head del fichero application/layouts/scripts/layout.phtml:

<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
<?php
$this->loadSkin($this->skin);
echo $this->headMeta();
echo $this->headTitle ();
echo $this->headScript (); 
echo $this->headLink ();
?>
</head>

Podemos ver el resultado navegando en http://localhost:


Realmente no veremos muchos cambios, si acaso la tipografía ya que hemos incluido un estilo específico para la misma.
Nota: Como detalle observaremos que el título de la página no se muestra. Esto es debido a la codificación UTF-8, si en lugar de  $view->headTitle('Centro de Documentación'); fuera  $view->headTitle('Centro de Documentacion'); el título sí se mostraría. Más adelante veremos como solucionar este problema.


Diseñando nuestro portal

El diseño puede llegar a ser fustrante sobretodo cuando todos los esfuerzos se centran en la programación. En mi opinión es preferible separar ambos mundos: la programación y el diseño. Es común tener una apariencia estándar del portal, para ello el uso de plantillas (skins) nos permitirá separar la representación de la lógica de la aplicación.
Primero definiremos un esqueleto genérico para nuestro portal, en nuestro caso un patrón de dos columnas con cabecera y pie, y una sección destinada a un menú rápido. He tomado como ejemplo las múltiples plantillas gratuitas que circulan por Internet, concretamente la usada aquí puede encontrarse en http://www.maxdesign.com.au/articles/css-layouts/two-liquid/

Seguiremos los siguientes pasos:
  • Header: Cabecera del portal, logo, motor de búsqueda, etc.
  • Navigation: Menú de acceso rápido.
  • Aside: Menú lateral de usuario.
  • Main Content: Contenido del portal.
  • Footer: Pie del portal, contacto, copyright, etc.
Para ello:
  1. Crearemos una carpeta nueva en el directorio application llamada layouts.
  2. En la nueva carpeta creada añadiremos una subcarpeta llamada scripts.
  3. Crearemos el fichero layout.phtml en application/layouts/scripts.
  4. Crearemos una carpeta llamada css en public.
  5. Crearemos una carpeta llamada images en public, donde depositaremos el fondo de la columna derecha.
Contenido del fichero application/layouts/scripts/layout.phtml
<?php
echo $this->doctype ();
?>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
<?php
echo $this->headTitle ();
echo $this->headScript ();
// add a link to the site style sheet
$this->headLink ()->appendStylesheet ( '/css/layout.css' );
echo $this->headLink ();
?>
</head>
<body>
 <div id="container">
  <div id="header">
   <h1>Zend Framework CMS</h1>
  </div>
  <div id="navigation">
  <?php echo $this->layout()->nav;?>
 </div>
  <div id="content-container">
   <div id="content">
   <?php echo $this->layout()->content?>
  </div>
   <div id="aside">
   <?php echo $this->layout()->subNav;?> 
  </div>
   <div id="footer">
    <em>Powered by the Zend Framework</em>
   </div>
  </div>
 </div>
</body>
</html>
Contenido del fichero public/css/layout.css:

@CHARSET "ISO-8859-1";

#container {
 margin: 0 auto;
 width: 100%;
 background: #fff;
}

#header {
 background: #ccc;
 padding: 20px;
}

#header h1 {
 margin: 0;
}

#navigation {
 float: left;
 width: 100%;
 background: #333;
}

#navigation ul {
 margin: 0;
 padding: 0;
}

#navigation ul li {
 list-style-type: none;
 display: inline;
}

#navigation li a {
 display: block;
 float: left;
 padding: 5px 10px;
 color: #fff;
 text-decoration: none;
 border-right: 1px solid #fff;
}

#navigation li a:hover {
 background: #383;
}

#content-container {
 float: left;
 width: 100%;
 background: #FFF url(/images/layout-two-liquid-background.gif) repeat-y 68% 0;
}

#content {
 clear: left;
 float: left;
 width: 60%;
 padding: 20px 0;
 margin: 0 0 0 4%;
 display: inline;
}

#content h2 {
 margin: 0;
}

#aside {
 float: right;
 width: 26%;
 padding: 20px 0;
 margin: 0 3% 0 0;
 display: inline;
}

#aside h3 {
 margin: 0;
}

#footer {
 clear: left;
 background: #ccc;
 text-align: right;
 padding: 20px;
 height: 1%;
}

Hemos de incluir el directorio de layouts creado en nuestro fichero de configuración para que la aplicación cargue sus contenidos. Para ello editamos application/configs/application.ini y añadimos en la sección production la entrada :

resources.layout.layoutPath = APPLICATION_PATH "/layouts/scripts"

Para finalizar sustituiremos el contenido del portal por un texto más simple. Para ello editaremos el fichero application/views/scripts/index/index.phtml con el siguiente código:
<h2>Home</h2>
<p>Esta es la página Home por defecto...</p>

Podemos probar el resultado navegando en http://localhost:


Filosofía de ZendFramework

Zend Framework son un conjunto de librerías para el desarrollo de aplicaciones PHP creadas por al empresa Zend. Zend está detrás de Zend Framework pero son muchas más las empresas que han aportado para que este proyecto evolucione y tenga una continuidad en el tiempo.
Zend Framework se basa en una arquitectura Modelo-Vista-Controlador.



  • Modelo: Ofrece una capa de abstracción sobre los datos de nuestra aplicación, aporta las funcionalidades básicas de rutinas de acceso a los datos.
  • Vista: Es la encargada de la representación de los datos de la capa Modelo, es la encargada de aportar las funcionalidades básicas de interacción de los usuarios y nuestra aplicación.
  • Controlador: Es el encargado de gestionar las peticiones de interacción con nuestras vistas y el árbitro que gestiona el flujo de datos de nuestra aplicación.
A grandes rasgos podemos decir que cada modelo que define nuestra lógica de la aplicación tendrá un controlador asociado, el cual nos definirá las posibles acciones que sobre el modelo de datos se podrá interaccionar, así como las diferentes vistas en forma de HTML que tendrá como resultado la ejecución de las acciones del controlador.

La arquitectura de directorios básica de Zend Framework tiene como elementos principales:
  • application: es donde tendremos ubicados todos los elementos de la arquitectura Modelo-Vista-Controlador. Dentro encontraremos las carpetas models, viewscontrollers, correspondientes a la ubicación de los modelos, vistas y controladores. Además en application encontraremos la carpeta configs donde definiremos los parámetros de configuración de la aplicación.
  • docs: Destinado a la documentación de nuestra aplicación.
  • library: En esta carpeta irá el código de nuestras librerías, eso incluye si se desea Zend Framework, aunque es más flexible tener Zend Framework en una ubicación común a todas las aplicaciones y definir su ruta en el include_path del php.ini.
  • public: Carpeta donde irán ubicados todos los archivos requeridos por el servidor al servir las vistas, tales como ficheros css, imágenes, etc.
  • test: Directorio destinado a los scripts de prueba, pueden escribirse a mano, test PHPUnit, etc.
Cabe destacar dentro del directorio public el archivo index.php, es el encargado de iniciar toda la aplicación instanciando un objeto del tipo Zend_Application y lanzando el método run de la clase Bootstrap.
La clase Bootstrap está en la raíz del directorio application, en el fichero Bootstrap.php, su propósito es inicializar la aplicación, ajustar las preferencias del entorno (la zona horaria, el nivel del error_reporting, etc), además de otros ajustes específicos al procesamiento que hace la aplicación de las vistas como por ejemplo la codificación.



Integración con Zend Studio Client

Ahora que ya hemos creado nuestro proyecto vamos a integrarlo con nuestro entorno de desarrollo. Yo personalmente uso Zend Studio Client, y en esta entrada explicaré como integrarlo con nuestro proyecto ya creado.

En Zend Studio Client seleccionamos del menú File -> New -> PHP Project from Existing Directory.
Inmediatamente nos mostrará un cuadro de diálogo para especificar el nombre del proyecto, en nuestro caso CeDocCMS, que es el nombre de la carpeta creada anteriormente. Recordemos que el proyecto se creó en la carpeta C:\Zend\Apache2\htdocs



Al especificar el proyecto seleccionaremos "Detect Local" para que sea el mismo Studio Client quien interaccione con nuestro servidor local.

 Al detectar la instalación local de nuestro ZendServer nos solicitará contraseña de administrador.


Al finalizar la instalación podemos seleccionar los paquetes de librerías que añadiremos, este es un paso opcional que más adelante pueden ser añadidas.


Finalmente disponemos de nuestro proyecto para trabajar con él desde Zend Studio.


Zend Server - Creando el proyecto

La elección de la carpeta destino de nuestro proyecto depende de nuestros gustos, más adelante definiremos un host vitual en la configuración de Apache para acceder a nuestro portal, así que en principio no nos debe preocupar mucho. Si trabajamos con un IDE como Zend Studio Client al hacer una distribución del proyecto (Deploy) o una depuración veremos que Zend Server usará unas carpetas diferentes.
Así pues nuestros proyectos los prodemos ubicar en la carpeta C:\Zend\Apache\htdocs.
Nota: Podemos escoger nuestra carpeta de proyectos "workspace", yo es la que suelo utilizar, principalmente porque la carpeta htdocs ubicaré otros portales ajenos a Zend como por ejemplo phpMyAdmin, y así no mezclar nuestros proyectos con otros portales. Para mayor comodidad en las rutas del proyecto de aquí en adelante usaré htdocs.

Hay varias formas de crear un proyecto. Si hemos instalado ZendServer es tan sencillo como desde línea de comandos, estando en la carpeta de proyectos ejecutar la herramienta de Zend (Zend Tool Framework):
zf create project prueba

El desarrollo de aplicaciones en Zend se basa en el uso de las librerías de ZendFramework, en el caso de tener instalado ZendServer el sistema ya tiene una referencia a las carpetas de las librerías de ZendFramework, ubicadas en C:\Zend\ZendServer\share\ZendFramework, debemos tener configurada la la variable include_path con ruta de las librerías en nuestro php.ini
include_path=".;c:\Zend\ZendServer\bin\pear;C:\Zend\ZendServer\share\ZendFramework\library"
Otra opción es descargarse las librerías de Zend y copiarlas directamente en la carpeta library de nuestro proyecto.

Para más información consultar Create Your Project.

Para nuestro proyecto usaremos la herramienta antes mencionada zf.bat de ZendServer, que nos creará automáticamente la estructura de directorios para nuestro proyecto CeDocCMS:

C:\Zend\Apache\htdocs>zf create project CeDocCMS

Vemos que al ejecutar el comando se ha creado la siguiente estructura de directorios:

CeDocCMS
|-- application 
|   |-- Bootstrap.php
|   |-- configs
|   |   `-- application.ini
|   |-- controllers
|   |   |-- ErrorController.php
|   |   `-- IndexController.php
|   |-- models
|   `-- views
|       |-- helpers
|       `-- scripts
|           |-- error
|           |   `-- error.phtml
|           `-- index
|               `-- index.phtml
|-- library
|-- public
|   |-- .htaccess
|   `-- index.php
`-- tests
    |-- application
    |   `-- bootstrap.php
    |-- library
    |   `-- bootstrap.php
    `-- phpunit.xml

Ahora crearemos un host virtual en Apache que apunte a nuestro proyecto. El fichero donde definiremos nuestros hosts virtuales será conf/extra/httpd-vhosts.conf, pero antes será necesario incluir dicho fichero en la configuración de Apache para que lo tenga en cuenta.
Editaremos C:\Zend\Apache2\conf\httpd.conf y quitaremos los comentarios a la línea donde está definido httpd-vhosts.conf.
# Virtual hosts
Include conf/extra/httpd-vhosts.conf
En el fichero de hosts virtuales añadiremos la entrada de nuestro hosts.
Nota: Hemos de editar el fichero httpd-vhosts.conf puesto que hay hosts virtuales de ejemplo que hemos de eliminar.
Nota: Al crear nuestro proyecto con la Zend Tool Framework en el directorio C:\Zend\Apache2\htdocs\CeDocCMS\docs tenemos a nuestra disposición el fichero README.txt con la definición de ejemplo de nuestro hosts virtual.

<VirtualHost *:80>
   DocumentRoot "C:/Zend/Apache2/htdocs/CeDocCMS/public"
   ServerName .local

   # This should be omitted in the production environment
   SetEnv APPLICATION_ENV development   ErrorLog "logs/CeDodCMS.example.com-error.log"
   CustomLog "logs/CeDodCMS.example.com-access.log" common

   <Directory "C:/Zend/Apache2/htdocs/CeDocCMS/public">
       Options Indexes MultiViews FollowSymLinks
       AllowOverride All
       Order allow,deny
       Allow from all
   </Directory>

</VirtualHost>
Nota: Añadiremos al ejemplo del README.txt la definición personalizada de la generación de logs.

Reiniciaremos el servidor para aplicar los cambios realizados. Si ahora consultamos nuestro localhost veremos el proyecto en funcionamiento:



lunes, 10 de febrero de 2014

Zend - Ultimando la instalación de Zend Server

Para finalizar la instalación del servidor es conveniente instalar PEAR. Para ello desde la línea de comandos podemos ejecutar:

c:\>cd c:\Zend\ZendServer\bin
c:\Zend\ZendServer\bin>go-pear.bat


Pulsaremos Enter y seleccionaremos todos los directorios por defecto.
Nota: Nos añadirá una entrada en nuestro archivo php.ini con el directorio del PEAR.

;***** Added by go-pear

include_path=".;c:\Zend\ZendServer\bin\pear;C:\Zend\ZendServer\share\ZendFramework\library"

;*****
Ahora podemos instalar PHPUnit
c:\Zend\ZendServer\bin>cd pear
C:\Zend\ZendServer\bin\PEAR>pear config-set auto_discover 1
Mostrará un mensaje del estilo "config-set succeeded". Seguidamente ejecutamos la instalación de PHPUnit:
C:\Zend\ZendServer\bin\PEAR>pear install pear.phpunit.de/PHPUnit

Nota: En algunas circunstancias valora la posibilidad de reinicar el equipo para que los cambios surgan efecto. En el caso de Zend Server es posible que muestro un error de configuración en la variable include_path, dado que reconoce que su valor difiere del que tiene almacenado. El mensaje podría tener la siguiente forma:
The directive 'include_path' is mismatched: expected '".;C:\Zend\ZendServer\share\ZendFramework\library"', actual '".;c:\Zend\ZendServer\bin\pear;C:\Zend\ZendServer\share\ZendFramework\library"'

Aplicaríamos cambios y reiniciaríamos el servidor Zend.

Zend - Primeros pasos

Todo proyecto informático nace de una necesidad, en concreto la mía es hacer una pequeña aplicación en PHP para la creación de un inventario para una biblioteca, y para ello me serviré de ZendFrameWork.

Consideraciones previas

En estos ejemplos usaré una instalación fresca de ZendServer y como IDE de desarrollo ZendStudio 10.6. Todos los ejemplos están hechos en una máquina Windows, más adelante abordaré la carga del código en una máquina de producción Linux.
En el primer caso no es necesario que exista pues con el esqueleto de Zend FrameWork sería suficiente y en el caso del IDE es a gusto del consumidor.
ZendServer se ha instalado en la raíz del sistema, así que su ruta de acceso es "C:\Zend", así pues todos los proyectos estarán ubicados en "c:\Zend\Apache2\htdocs".

Empezando

Instalación de ZendServer. Para descargarlo es necesario tener un usuario registrado en http://www.zend.com/en/downloads/, allí seleccionaremos Zend Server. En el momento de escribir esta entrada la versión de Zend Server era 6.2.0. La versión de PHP es a gusto del usuario, yo instalé la 5.4
Nota: Sería recomendable instalar también MySQL

La primera pantalla es la de bienvenida:

La segunda hace referencia a la licencia, aceptamos si estamos de acuerdo y proseguimos.
 En la tercera pantalla elegiremos la opción "custom":

En la siguiente pantalla buscaremos la opción de instalación de MySQL Server y lo seleccionaremos:

Nota: podría ser interesante añadir más productos como por ejemplo IBM DB2 RTCL para acceder a sistemas iSeries.
En la siguiente pantalla seleccionaremos el path de instalación, para hacerlo más cómo ubicaremos a Zend Server en la raíz.

La siguiente pantalla nos pide los puertos de escucha para el servicio de páginas web y la administración del servidor. Atención si en nuestra máquina ya existen servidores web que usen esos puertos, quizás el puerto 80 debamos cambiarlo por 10080.

Instalación final pulsamos "Install":

Al finalizar la instalación nos permitirá ejecutar el servidor instalado, a lo cual respondemos que sí seleccionando la opción, inmediatamente nos abrirá un navegador con la url del servidor. Aceptaremos el contrato de licencia si estamos de acuerdo.
En la siguiente pantalla decidiremos si nuestro servidor será de desarrollo o producción, en mi caso fundamentalmente será desarrollo.
En la siguiente pantalla se nos solicita las contraseñas.

Finalmente si la instalación ha ido correctamente podremos ver el resultado:
Si consultamos el localhost ahora aparecerá la página de inicio de ZendServer: