34

Comenzando con ZEND framework

Embed Size (px)

DESCRIPTION

manual

Citation preview

Page 1: Comenzando con ZEND framework
Page 2: Comenzando con ZEND framework

Comenzando con Zend Framework 2

eminetto y Antonio Garcia

Este libro está a la venta en http://leanpub.com/comenzando-zf2

Esta versión se publicó en 2014-12-29

This is a Leanpub book. Leanpub empowers authors and publishers with the Lean Publishingprocess. Lean Publishing is the act of publishing an in-progress ebook using lightweight tools andmany iterations to get reader feedback, pivot until you have the right book and build traction onceyou do.

©2014 eminetto y Antonio Garcia

Page 3: Comenzando con ZEND framework

Índice general

Introdución . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1

Instalando . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2Requisitos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2Instalando el framework . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3

Definiendo el proyecto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6Descripición . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6Modelado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6Creación de las tablas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6Configurando el proyecto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8

Modelos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10Creando a entidad Post . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10

Controladores, rutas y vistas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14Controladores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14Vistas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15Layouts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17Rutas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20Desafío . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21

Formularios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22

Próximos pasos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31

Page 4: Comenzando con ZEND framework

IntroduciónLa idea de este libro es mostrar los “primeros pasos” en el mundo de Zend Framework 2. No vamosa profundizar en tecnicas complejas para construir proyectos con Zend Framework 2, pero vamos aver como crear un CRUD básico y entender algunos conceptos importantes.

En el ultimo capitulo del libro veremos cuales serian los proximos pasos a dar, para donde ir despuésde esta primera toma de contacto.

¿Empezamos?

Page 5: Comenzando con ZEND framework

InstalandoEl proceso de instalacion de Zend Framework 2 fue uno de los asuntos que más ha mejorado desdeversiones anteriores. La instalación y actualización del framework y sus dependencias es realmentemuy fácil.

Requisitos

Para crear un proyecto que utilice Zend Framework 2 necesitamos cumplir los siguiente requisitos:

• Un servidor Web. El más utilizado es Apache aunque se pueden utilizar otros como _IIS oNginx. Los ejemplos de este libro se basan en el servidor Apache. PHP, a partir de la version5.4, incluye un servidor web embebido, pero no consideré usarlo para este libro puesto que notodos los ambientes de desarrollo estan actualizados con las versiones más actualizadas. En elcaso de utilizar Apache es necesario que el módulo mod_rewrite esté habilitado. En el ficherode configuración bastará añadir las lineas de abajo, o modificarlas para obtener lo siguiente:

1 LoadModule rewrite_module modules/mod_rewrite.so

2 AddModule mod_rewrite.c

3 AllowOverride all

• Un sistema gestor base de datos. No es obligaorio pero en nuestro caso vamos utilizarMySQL.Puedes utilizar otro gestor de base de datos como SQLite o PostgreSQL, aunque los ejemplosestán escritos para MySQL.

• PHP 5.3.3 o superior.• Extensión intl de PHP. El framework usa esta extensión para formatear fechas y numeros.Esta extensión puede instalarse usando comando pecl de PHP.

Si estamos usandoWindows oMacOSX estos requisitos pueden ser facilmente cumplidos instalandoun de los paquetes de desarrollo famosos comoXAMPP¹ (Windows yMacOSX ) oMAMP² (MacOSX),que incluyen todos los paquetes configurados.

Si utiliza Linux bastará usar el sistema de gestión de paquetes (apt-get, yum, etc) para instalar lospaquetes necesarios.

¹https://www.apachefriends.org/index.html²http://www.mamp.info/en/index.html

Page 6: Comenzando con ZEND framework

Instalando 3

Instalando el framework

La forma recomendada de crear un proyecto es usar uno de los de los “esqueletos de aplicacion” queestan disponibles en Github. La documentación oficial del framework³ recomienda el uso de:

https://github.con/zendframework/ZendSkeletonApplication⁴

Para comenzar nuestro proyecto vamos clonar el proyecto usando git. El primer paso es acceder anuestro directorio de proyectos. En mi MacOSX ese directorio es /Users/eminetto/Documents/Pro-jects/ . En tu caso puede ser cualquier otro directorio.

Ejecutamos los comandos siguientes:

1 cd /Users/eminetto/Documents/Projects/

2 git clone https://github.con/zendframework/ZendSkeletonApplication.git empezando\

3 -zf2

Esto crea un directorio empezando-zf2 con el codigo del esqueleto.

Si no tuvieras git instalado en tu máquina puedes descargar y descomprimir en el directorio. Ladescarga puedes hacerla desde la url:

https://github.con/zendframework/ZendSkeletonApplication/archive/master.zip⁵

Instalar dependencias con Composer

Al clonar (o realizar la descarga) del esqueleto de la aplicación nos tendriamos completamenteel framework. La forma más rapida de tener el framework instalado completamente seria usandola herramienta Composer. Esta herramienta fue creada para instalar y actualizar dependencias decódigo en proyectos PHP. Para entender con detalle como funciona Composer te recomiendo estescreencast⁶ y la web oficial⁷ de la herramienta. Nota del traductor: En español podriamos visitarscreencast⁸ de Desarrolloweb.con para una introducción a Composer.

Vamos usar Composer para terminar de instalar el framework:

1 cd empezando-zf2

2 php composer.phar self-update

3 php composer.phar install

³http://framework.zend.con/manual/2.0/en/user-guide/skeleton-application.html⁴https://github.con/zendframework/ZendSkeletonApplication⁵https://github.con/zendframework/ZendSkeletonApplication/archive/master.zip⁶http://code-squad.con/screencast/composer⁷http://getcomposer.org⁸https://www.youtube.con/watch?v=fAxWK2C4Buw

Page 7: Comenzando con ZEND framework

Instalando 4

El primer comando, self-update no es obligatorio pero es recomendable pues te garantiza queComposer está actualizado con a última versión estable. Con el segundo comando Composerrealizará la descarga del framework y todas sus dependencias, además de configurar un autoloaderque el framework usará.

Esto es mucho más rápido y fácil que las versiones antiguas, que pedian registrarse en la web deZend.

Configurar Vhosts de Apache

Un hábito que tengo siempre que desarrollo un nuevo proyecto es crear un servidor virtual en mimáquina para aislar el entorno del proyecto. Esto facilita bastante las pruebas, la organización delos proyectos es hasta el mismo deploy de la aplicación en el servidor de producción al final deldesarrollo.

Para esto vamos configurar un servidor virtual en Apache. En el archivo httpd.conf (o apache.confo la configuración de servidores virtuales de tu sistema operativo) añadir lo siguiente:

1 <VirtualHost *:80>

2 ServerName empezando-zf2.dev

3 DocumentRoot /Users/eminetto/Documents/Projects/empezando-zf2/public

4 SetEnv APPLICATION_ENV "development"

5 SetEnv PROJECT_ROOT "/Users/eminetto/Documents/Projects/empezando-zf2"

6 <Directory /Users/eminetto/Documents/Projects/empezando-zf2/public>

7 DirectoryIndex index.php

8 AllowOverride All

9 Order allow,deny

10 Allow from all

11 </Directory>

12 </VirtualHost>

https://gist.github.con/eminetto/318b15ff2554a1db58cd⁹

Es necesario modificar las rutas en las opciones DocumentRoot, PROJECT_ROOT y Directory conla ruta correcta en su máquina. Tambien modificaremos el archivo hosts de sistema operativo paraañadir la dirección iniciando-zf2.dev puesto que no existe ningún DNS.

En Linux y Mac OSX, modificamos /etc/hosts añadiendo la línea:

1 127.0.0.1 empezando-zf2.dev

⁹https://gist.github.con/eminetto/318b15ff2554a1db58cd

Page 8: Comenzando con ZEND framework

Instalando 5

EnWindows el archivo que debe ser modificador es c:\windows\system32\drivers\etc\hosts y la líneapara añadir es la misma para los otros sistemas operativos.

Ahora podemos realizar una prueba para verificar si nuestro esqueleto de proyecto está funcionandoaccediendo a la dirección: http://empezando-zf2.dev em algún navegador. La pantalla esperada es lamostrada más abajo.

Welcome

Esta es la pantalla de bienvenida de Zend Framework 2. En el caso de que esta pantalla no esteapareciendo recomiendo repasar los pasos anteriores y tambien comprobar los logs de Apachepara identificar que puede estar incorrecto. Normalmente la configuración del virtual host es ella causante de los fallos.

Con estos pasos tenemos el entorno instalado y podemos iniciar la planificación de nuestro primerproyecto utilizando Zend Framework 2.

Page 9: Comenzando con ZEND framework

Definiendo el proyectoDescripición

En mi opinión la mejor forma de aprender una nueva herramienta, lenguaje o sistema operativo escuando necesitas realmente resolver algún problema con ella. Pensando en eso, este libro esta basadoen la construcción de una aplicación: un blog.

¿Porque un blog? Por algunos motivos:

• Es un problema fácil de entender. Todo mundo sabe como funciona un blog, sus requisitos yfuncionalidades, y así la fase de toma de requisitos del proyecto es fácil de completar.

• Un blog posee un gran número de funcionalidades comunes con otras aplicaciones, comomódulos, control de acesso y permisos, subida de archivos, tratamento de formularios, cache,traduciones, integración con servicios externos, etc.

• La gran mayoria de los frameworks poseen un ejemplo “como desarrollar un blog usandoX”, así resulta más fácil comparar si ya conocias algún otro framework como CakePHP,CodeIgniter o incluso Ruby on Rails

Modelado

Ahora que te convenci (o no) que desarrollar un blog te puede ayudar entender Zend Framework,vamos a ver el modelado de tablas:

Modelagem

Simples, como deberia ser.

Creación de las tablas

Usando alguna herramienta, como PHPMyAdmin, SequelPro, o el terminal, es posible crear laestructura de datos usando los siguientes comandos SQL:

Page 10: Comenzando con ZEND framework

Definiendo el proyecto 7

1 create database empezandozf2;

2

3 GRANT ALL privileges ON empezandozf2.* TO zend@localhost IDENTIFIED BY 'zend';

4

5 use empezandozf2;

6

7 CREATE TABLE IF NOT EXISTS `users` (

8 `id` INT NOT NULL AUTO_INCREMENT ,

9 `username` VARCHAR(200) NOT NULL ,

10 `password` VARCHAR(250) NOT NULL ,

11 `name` VARCHAR(200) NULL ,

12 `valid` TINYINT NULL ,

13 `role` VARCHAR(20) NULL ,

14 PRIMARY KEY (`id`) )

15 ENGINE = InnoDB;

16

17 CREATE TABLE IF NOT EXISTS `posts` (

18 `id` INT NOT NULL AUTO_INCREMENT ,

19 `title` VARCHAR(250) NOT NULL ,

20 `description` TEXT NOT NULL ,

21 `post_date` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ,

22 PRIMARY KEY (`id`) )

23 ENGINE = InnoDB;

24

25 CREATE TABLE IF NOT EXISTS `comments` (

26 `id` INT NOT NULL AUTO_INCREMENT ,

27 `post_id` INT NOT NULL ,

28 `description` TEXT NOT NULL ,

29 `name` VARCHAR(200) NOT NULL ,

30 `email` VARCHAR(250) NOT NULL ,

31 `webpage` VARCHAR(200) NOT NULL ,

32 `comment_date` TIMESTAMP NULL ,

33 PRIMARY KEY (`id`, `post_id`) ,

34 INDEX `fk_comments_posts` (`post_id` ASC) ,

35 CONSTRAINT `fk_comments_posts`

36 FOREIGN KEY (`post_id` )

37 REFERENCES `posts` (`id` )

38 ON DELETE NO ACTION

39 ON UPDATE NO ACTION)

40 ENGINE = InnoDB;

Page 11: Comenzando con ZEND framework

Definiendo el proyecto 8

https://gist.github.con/eminetto/284eddfbc1370c113ee2¹⁰

Configurando el proyecto

Zend Framework 2 cuenta con archivos de configuración separados que son unificados en elmomento de ejecución.

Los principales archivos que vamos usar durante el proyecto son:

• config/application.config.php: Archivo con las configuraciones generales de la aplicación. Sonconfiguraciones usadas por todos los módulos y componentes.

• config/autoload/global.php y config/autoload/local.php: El archivo global.php es usado comocomplemento de application.config.php pues también contiene configuraciones de la aplica-ción y funcionan como un todo. La idea es colocar en este archivo configuraciones que puedencambiar de acuerdo con la máquina del desarrollador. Un ejemplo son las configuraciones deconexión con la base de datos. Estas configuraciones pueden ser modificados para las máqui-nas locales, de los desarrolladores. Para eso el desarrollador sobreescribe las configuracionesen local.php. El archivo local.php no debe ser guardado en el control de versiones (svn o gitpor ejemplo).

• _module/

Los archivos de configuración son generalmente scripts PHP que devuelven arrays de configuración.Son rápidos durante la ejecución y de fácil lectura.

global.php

1 <?php

2 return array(

3 'service_manager' => array(

4 'factories' => array(

5 'Zend\Db\Adapter\Adapter' => 'Zend\Db\Adapter\AdapterServiceFactory',

6 ),

7 ),

8 'db' => array(

9 'driver' => 'Pdo',

10 'dsn' => 'mysql:dbname=empezandozf2;host=localhost',

11 'driver_options' => array(

12 PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\''

13 ),

14 )

15 );

¹⁰https://gist.github.con/eminetto/284eddfbc1370c113ee2

Page 12: Comenzando con ZEND framework

Definiendo el proyecto 9

https://gist.github.con/eminetto/24d2462ed652ca7ffc14¹¹

local.php

1 <?php

2 return array(

3 'db' => array(

4 'username' => 'zend',

5 'password' => 'zend',

6 )

7 );

https://gist.github.con/4011983¹²

Volvermos a ver estos archivos de configuración en el transcurso del proyecto, y sus elementos pa-saran a tener más sentido conforme vayamos aprendiendo algunas funcionalidades del framework.

Projeto definido y configurado. Ahora, manos a la obra!

¹¹https://gist.github.con/eminetto/24d2462ed652ca7ffc14¹²https://gist.github.con/4011983

Page 13: Comenzando con ZEND framework

ModelosVamos a comenzar el desarrollo por la primera capa de nuestra aplicación MVC: los modelos. Paraesto vamos crear un archivo para cada tabla y estos deben ser guardados en el directorio Model desrc de módulo, como por ejemplo: module/Application/src/Application/Model .

Creando a entidad Post

El primer paso es crear el directorio (en caso no exista) de modelos:

1 mkdir module/Application/src/Application/Model

Creamos dentro de este el archivo Post.php con el siguiente contenido:

1 <?php

2

3 namespace Application\Model;

4

5 class Post

6 {

7 public $id;

8 public $title;

9 public $description;

10 public $post_date;

11

12 //usado por TableGateway

13 public function exchangeArray($data)

14 {

15 $this->id = (!empty($data['id'])) ? $data['id'] : null;

16 $this->title = (!empty($data['title'])) ? $data['title'] : null;

17 $this->description = (!empty($data['description'])) ? $data['descripti\

18 on'] : null;

19 $this->post_date = (!empty($data['post_date'])) ? $data['post_date'] :\

20 null;

21 }

22 }

Page 14: Comenzando con ZEND framework

Modelos 11

https://gist.github.con/eminetto/a38dc854af21e32447e4¹³

El primer punto importante a ser destacado es el uso del concepto deNamespaces. Zend Framework 2usa extensivamente el concepto de Namespaces que fue introducido en PHP 5.3. En la primera líneaindicamos cual espacio donde el código que vamos escribir pertenece, siendo obligatorio siempreutilizar uno. En las próximas líneas podemos indicar los componentes que vamos a nacesitar y a quenamespaces pertenecen usando el comando use (vamos ver esto en las próximas páginas). Haciendoesto las clases son automáticamente cargadas, sin necesidad de preocuparnos en usar include orequire, dejando esta tarea para el mecanismo de autoloading implementado por el framework. Másinformación sobre namespaces pueden ser consultadas en el manual de PHP¹⁴.

La clase Post es simplemente una representación de los datos de la base de datos, pero necesita sermanipulada por alguna otra clase. Podemos usar dos herramientas para hacer esa manipulación: unTableGateway o un ORM como Doctrine. A función de un TableGateway es realizar operacionessobre entidades pues ellas no poseen comportamiento. Las principales operaciones como insertar,eliminar, actualizar, buscar serán siempre realizadas através de este gateway. Existen muchasventajas de esta aproximación como poder cambiar la capa de entidades (substituir por entidades deORMDoctrine por ejemplo, que no vamos ver en este libro) o cambiar el comportamiento de todas lasentidades simplemente alterando los gateways. Vamos ahora crear un TableGateway para manipularla clase Post con el archivo module/Application/src/Application/Model/PostTableGateway.php:

1 <?php

2 namespace Application\Model;

3

4 use Zend\Db\TableGateway\TableGateway;

5

6 class PostTableGateway

7 {

8 protected $tableGateway;

9

10 public function __construct(TableGateway $tableGateway)

11 {

12 $this->tableGateway = $tableGateway;

13 }

14

15 public function fetchAll()

16 {

17 $resultSet = $this->tableGateway->select();

18 return $resultSet;

19 }

20

¹³https://gist.github.con/eminetto/a38dc854af21e32447e4¹⁴http://php.net/manual/es/language.namespaces.php

Page 15: Comenzando con ZEND framework

Modelos 12

21 public function get($id)

22 {

23 $id = (int) $id;

24 $rowset = $this->tableGateway->select(array('id' => $id));

25 $row = $rowset->current();

26 if (!$row) {

27 throw new \Exception("No encontrado id $id");

28 }

29 return $row;

30 }

31

32 public function save(Post $post)

33 {

34 $data = array(

35 'title' => $post->title,

36 'description' => $post->description,

37 'post_date' => $post->post_date,

38 );

39

40 $id = (int) $post->id;

41 if ($id == 0) {

42 $this->tableGateway->insert($data);

43 } else {

44 if ($this->get($id)) {

45 $this->tableGateway->update($data, array('id' => $id));

46 } else {

47 throw new \Exception('Post no existe');

48 }

49 }

50 }

51

52 public function delete($id)

53 {

54 $this->tableGateway->delete(array('id' => (int) $id));

55 }

56 }

https://gist.github.con/eminetto/0d5e1e5b11a53523ee36¹⁵

Como podemos ver en el código anterior, la clase PostTableGateway necesita de una instancia dela clase TableGateway para ser creada, podemos decir que esta clase posee una dependencia. Asu vez la clase TableGateway también posee una série de dependencias, lo que comienza a tornar

¹⁵https://gist.github.con/eminetto/0d5e1e5b11a53523ee36

Page 16: Comenzando con ZEND framework

Modelos 13

complejo el uso del sistema. Para simplificar estos casos el framework posee un componente muyútil llamado ServiceManager donde podemos registrar todas las dependencias de nuestras clases yel se encarga de crearlas par nosotros, siempre que necesitemos. En el archivo de configuracionesde nuestro módulo vamos incluir estas nuevas reglas de dependencias, modificando el archivomodule/Application/config/module.config.php:

1 'service_manager' => array(

2 'abstract_factories' => array(

3 'Zend\Cache\Service\StorageCacheAbstractServiceFactory',

4 'Zend\Log\LoggerAbstractServiceFactory',

5 ),

6 'aliases' => array(

7 'translator' => 'MvcTranslator',

8 ),

9 'factories' => array(

10 'Application\Model\PostTableGateway' => function($sm) {

11 $dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');

12 $resultSetPrototype = new Zend\Db\ResultSet\ResultSet();

13 $resultSetPrototype->setArrayObjectPrototype(new Application\Mod\

14 el\Post());

15 $tableGateway = new Zend\Db\TableGateway\TableGateway('posts’, $\

16 dbAdapter, null, $resultSetPrototype);

17 $postTableGateway = new Application\Model\PostTableGateway($tabl\

18 eGateway);

19 return $postTableGateway;

20 },

21 ),

22 ),

https://gist.github.con/eminetto/b6487feb6f0de85ada38¹⁶

Lo que estamos diciendo al framework es: “siempre que solicitemos el servicioApplication\Model\PostTableGatewayejecute esta función anónima con todos los pasos necesarios para su creación y me devuelva unainstancia lista para funcionar”. En el próximo capítulo vamos ver como usar el ServiceManager paraque nos devuelva la instancia de PostTableGateway configurada y lista para su uso.

Voy dejar a configuración de las otras tablas de la base de datos como ejercicio para el lector y vamosahora trabajar con la próxima capa, los controladores.

¹⁶https://gist.github.con/eminetto/b6487feb6f0de85ada38

Page 17: Comenzando con ZEND framework

Controladores, rutas y vistasControladores

Ls controladores son responsables de la interacción con el usuario, interceptando las invocacionespor urls, clicks en links, etc. El controlador recibirá la acción del usuario, ejecutar algún servicio,manipular alguna clase de la capa de modelo y, generalmente, renderizar una pantalla de la capa dela vista, para generar una respuesta para el usuario.

El primer controlador que crearemos es el responsable de mostrar todos los posts de nuestra tabla,para que el usuario los visualice.

Para crear un controlador necesitamos escribir una clase que implemente la interfaz Dispatchable.Zend Framework proporciona algunas clases que implementan esta interfaz, facilitando para nuestrouso, como AbstractActionController y AbstractRestfulController.

Vamos a crear el controlador, en el archivomodule/Application/src/Application/Controller/PostCon-troller.php:

1 <?php

2 namespace Application\Controller;

3

4 use Zend\View\Model\ViewModel;

5 use Zend\Mvc\Controller\AbstractActionController;

6

7 /**

8 * Controlador que gestiona los posts

9 *

10 * @category Application

11 * @package Controller

12 * @author Elton Minetto <[email protected]>

13 */

14 class PostController extends AbstractActionController

15 {

16 /**

17 * Muestra los posts guardados

18 * @return void

19 */

20 public function indexAction()

21 {

Page 18: Comenzando con ZEND framework

Controladores, rutas y vistas 15

22 $tableGateway = $this->getServiceLocator()->get('Application\Model\PostTabl\

23 eGateway');

24 $posts = $tableGateway->fetchAll();

25 return new ViewModel(array(

26 'posts' => $posts

27 ));

28 }

29 }

https://gist.github.con/eminetto/86e810dac94a3f761d73¹⁷

Podemos ver ahora el uso del ServiceManager para obtener una instancia de PostTableGateway,conforme a la configuración del capítulo anterior y al uso del método fetchAll() que devuelve todoslos registros de la tabla. indexAction crea una instancia de la clase ViewModel y la configura conuna variable llamada posts con el resultado de la consulta de la base de datos. Al devolver unainstancia de ViewModel estamos solicitando al framework que renderize el HTML de la vista. Comono indicamos alguna vista específica se renderizara la vista por defecto. Como estamos accediendo ala action index del controlador PostController el framework buscará un archivo llamado index.phtmlen el directorio: module/Application/view/application/post/.

Vistas

A pesar de la extensión diferente (.phtml) la vista no deja de ser un archivo PHP donde podemosusar todas las funciones nativas del lenguaje y algunos componentes especiales,llamados de ViewHelpers.

El contenido de archivo index.phtml sería:

1 <div class="actions clearfix">

2 <div class="btns">

3 <a class="btn btn-primary" href="/post/save" title="Crear Post">

4 Crear Post

5 </a>

6 </div>

7 </div>

8 <h3>Lista de Posts</h3>

9 <table class="table table-striped table-bordered">

10 <thead>

11 <tr>

12 <th>Título</th>

13 <th>Texto</th>

¹⁷https://gist.github.con/eminetto/86e810dac94a3f761d73

Page 19: Comenzando con ZEND framework

Controladores, rutas y vistas 16

14 <th width="130" class="center">Fecha</th>

15 <th width="120" class="center">Opciones</th>

16 </tr>

17 </thead>

18 <tbody>

19 <?php foreach($posts as $post):?>

20 <tr>

21 <td><?php echo $this->escapeHtml($post->title);?></td>

22 <td><?php echo $this->escapeHtml($post->description);?></td>

23 <td class="center">

24 <?php

25 echo $this->dateFormat(

26 \DateTime::createFromFormat('Y-m-d H:i:s', $post->post_date),

27 \IntlDateFormatter::SHORT,

28 \IntlDateFormatter::SHORT,

29 'es'

30 );

31 ?>

32 </td>

33 <td class="center">

34 <a href="/post/save/<?php echo $post->id ;?>"

35 title="Editar">

36 Editar

37 </a>

38 <a href="/post/delete/<?php echo $post->id;?>"

39 rel="confirmation"

40 title="Desea eliminar este registro?">

41 Eliminar

42 </a>

43 </td>

44 </tr>

45 <?php endforeach;?>

46 </tbody>

47 </table>

https://gist.github.con/eminetto/219a41d5be98784c6f89¹⁸

Algunos puntos importantes de index.phtml:

1 <?php foreach($posts as $post):?>

La variable posts es la misma que fue enviada por el controlador, es decir, la lista de registrosobtenidos de la tabla post.

¹⁸https://gist.github.con/eminetto/219a41d5be98784c6f89

Page 20: Comenzando con ZEND framework

Controladores, rutas y vistas 17

1 <?php echo $this->escapeHtml($post->title);?>

Este es un ejemplo del uso de un ViewHelper, escapeHtml, que limpia cualesquiera tags HTML quepuedan existir en el texto. Lo mismo vale para dateFormat que realiza un formateo de las fechas.

Layouts

En este momento cabe explicar un nuevo concepto: layouts. Zend Framework trabaja con la idea delayouts, que son estructuras o páginas base que fusionamos con las vistas de los controladores, comoa index.phtml.

Usando una imagem para ilustrar un ejemplo:

Wireframe mostrando un layout

En todas las páginas tendremos un título, una imagen y un pie, con información sobre el autor,copyright, etc. La única informacción que cambiará es el contenido de las páginas, como index.phtmlque acabamos de crear, pero la cabecera y el pie permaneceran sin cambios.

Podemos tener varios layouts y usaremos el más indicado en cada controlador o action. El códigodel layout por defecto de Zend Framework 2 se encuentra en el archivo module/Application/view/-layout/layout.phtml:

Page 21: Comenzando con ZEND framework

Controladores, rutas y vistas 18

1 <?php echo $this->doctype(); ?>

2

3 <html lang="en">

4 <head>

5 <meta charset="utf-8">

6 <?php echo $this->headTitle('ZF2 '. $this->translate('Skeleton Applicati\

7 on'))->setSeparator(' - ')->setAutoEscape(false) ?>

8

9 <?php echo $this->headMeta()

10 ->appendName('viewport', 'width=device-width, initial-scale=1.0')

11 ->appendHttpEquiv('X-UA-Compatible', 'IE=edge')

12 ?>

13

14 <!-- Le styles -->

15 <?php echo $this->headLink(array('rel' => 'shortcut icon', 'type' => 'im\

16 age/vnd.microsoft.icon', 'href' => $this->basePath() . '/img/favicon.ico'))

17 ->prependStylesheet($this->basePath() . '/css/style.css')

18 ->prependStylesheet($this->basePath() . '/css/bootstrap-\

19 theme.min.css')

20 ->prependStylesheet($this->basePath() . '/css/bootstrap.\

21 min.css') ?>

22

23 <!-- Scripts -->

24 <?php echo $this->headScript()

25 ->prependFile($this->basePath() . '/js/bootstrap.min.js')

26 ->prependFile($this->basePath() . '/js/jquery.min.js')

27 ->prependFile($this->basePath() . '/js/respond.min.js', 'text/javasc\

28 ript', array('conditional' => 'lt IE 9',))

29 ->prependFile($this->basePath() . '/js/html5shiv.js', 'text/javasc\

30 ript', array('conditional' => 'lt IE 9',))

31 ; ?>

32

33 </head>

34 <body>

35 <nav class="navbar navbar-inverse navbar-fixed-top" role="navigation">

36 <div class="container">

37 <div class="navbar-header">

38 <button type="button" class="navbar-toggle" data-toggle="col\

39 lapse" data-target=".navbar-collapse">

40 <span class="icon-bar"></span>

41 <span class="icon-bar"></span>

42 <span class="icon-bar"></span>

Page 22: Comenzando con ZEND framework

Controladores, rutas y vistas 19

43 </button>

44 <a class="navbar-brand" href="<?php echo $this->url('home') \

45 ?>"><img src="<?php echo $this->basePath('img/zf2-logo.png') ?>" alt="Zend Frame\

46 work 2"/>&nbsp;<?php echo $this->translate('Skeleton Application') ?></a>

47 </div>

48 <div class="collapse navbar-collapse">

49 <ul class="nav navbar-nav">

50 <li class="active"><a href="<?php echo $this->url('home'\

51 ) ?>"><?php echo $this->translate('Home') ?></a></li>

52 </ul>

53 </div><!--/.nav-collapse -->

54 </div>

55 </nav>

56 <div class="container">

57 <?php echo $this->content; ?>

58 <hr>

59 <footer>

60 <p>&copy; 2005 - <?php echo date('Y') ?> by Zend Technologies Lt\

61 d. <?php echo $this->translate('All rights reserved.') ?></p>

62 </footer>

63 </div> <!-- /container -->

64 <?php echo $this->inlineScript() ?>

65 </body>

66 </html>

https://gist.github.con/eminetto/3f5f0594afd63ec449c2¹⁹

Este archivo hace uso de varios ViewHelpers que no veremos ahora, como headLink o translate, yposee una estrutura más compleja de diseño y CSS. La parte más importante para nosotros ahora esla línea:

1 <?php echo $this->content; ?>

Este es el punto donde vista será renderizada en el layout, es decir, el contenido de index.phtmlsera mezclado con el restante. Como todos los controladores usan este layout, por defecto estecomportamiento será igual em todos, a menos que el desarrollador modifique la configuraciónde algún de ellos. El layout puede ser mucho mas simple, apenas necesitas constar esa línea, queimprime $this->content.

¹⁹https://gist.github.con/eminetto/3f5f0594afd63ec449c2

Page 23: Comenzando con ZEND framework

Controladores, rutas y vistas 20

Rutas

Por motivos de seguridad y organización los controladores que creamos no son automaticamenteaccesibles por los navegadores de los usuarios. Necesitamos primeramente habilitar el controladoren el módulo y despues crear una configuración de enrutado, la URL que usuario usara para acceder.

Primero vamos habilitar el controlador en el archivo de configuraciones de nuestro módulo, elmodule/Application/config/module.config.php, en la opción controllers :

1 'controllers' => array(

2 'invokables' => array(

3 'Application\Controller\Index' => 'Application\Controller\IndexContr\

4 oller',

5 'Application\Controller\Post' => 'Application\Controller\PostControl\

6 ler'

7 ),

8 ),

El próximo paso será crear la ruta.

Abrimos un pequeño paréntesis aqui, y comentar sobre como los controladores y actions funcionan.Generalmente los controladores son clases cuyo nombre termina en Controller como en PostCon-troller (a pesar de esto no es obligatorio en Zend Framework 2 aunque continua siendo estándar).Cada controlador posee una omás actions que sonmétodos públicos cuyo nombre termina enActioncomo en indexAction. Las actions son las acciones que los usuarios pueden acceder via URL, links obotones en la pantalla. Por ejemplo, en el caso el usuario accede a la url:

1 http://empezando-zf2.dev/admin/index/index/id/1

Esto es traducido por el framework usando o estándar:

1 http://servidor/modulo/controller/action/parametro/valor

Entonces:

• Servidor = empezando-zf2.dev• Módulo = Admin• Controller = IndexController.php• Action = indexAction (dentro de archivo IndexController.php)• Parámetro = id• Valor = 1

Page 24: Comenzando con ZEND framework

Controladores, rutas y vistas 21

Este es el comportamiento por defecto esperado por el framework pero nosotros podemos crearnuestras rutas que mejor se adapten a nuestro proyecto. Vamos a crear las siguientes rutas paranuestro pequeño proyecto:

• /post: será la lista de posts. La lógica estará en indexAction• /post/save: será inserción de posts. La lógica estará en saveAction• /post/save/id/NUMERO: será la edición de posts. La lógica también estará en saveAction• /post/delete/id/NUMERO: será la eliminación de posts. La lógica estará en deleteAction

Para eso vamos crear una nueva clave en el array router de archivo module/Application/config/mo-dule.config.php, despues de la clave application:

1 'post' => array(

2 'type' => 'segment',

3 'options' => array(

4 'route' => '/post[/][:action][/:id]',

5 'constraints' => array(

6 'action' => '[a-zA-Z][a-zA-Z0-9_-]*',

7 'id' => '[0-9]+',

8 ),

9 'defaults' => array(

10 'controller' => 'Application\Controller\Post',

11 'action' => 'index',

12 ),

13 ),

14 ),

https://gist.github.con/eminetto/ce1ce0286d6450ac5116²⁰

Ahora podemos acceder a la URL http://empezando-zf2.dev/post y visualizariamos los posts guarda-dos en la base de datos. Los botones aún no funcionan pues no creamos las nuevas actions. Haremosesto en el próximo capítulo.

Desafío

En este capítulo vimos el controlador, las vistas y las rutas para manipular la entidad Post. Comodesafío dejo al lector añadir una nueva funcionalidad: el listado de comentarios de un post.

²⁰https://gist.github.con/eminetto/ce1ce0286d6450ac5116

Page 25: Comenzando con ZEND framework

FormulariosZend Framework 2 posee un componente para ayudar en la creación de los formularios, Zend\Form.

Vamos a crear nuestro primer formulario, module/Application/src/Application/Form/Post.php :

1 <?php

2 namespace Application\Form;

3

4 use Zend\Form\Form;

5

6 class Post extends Form

7 {

8 public function __construct()

9 {

10 parent::__construct('post');

11 $this->setAttribute('method', 'post');

12 $this->setAttribute('action', '/post/save');

13

14 $this->add(array(

15 'name' => 'id',

16 'attributes' => array(

17 'type' => 'hidden',

18 ),

19 ));

20

21 $this->add(array(

22 'name' => 'title',

23 'attributes' => array(

24 'type' => 'text',

25 ),

26 'options' => array(

27 'label' => 'Título',

28 ),

29 ));

30 $this->add(array(

31 'name' => 'description',

32 'attributes' => array(

33 'type' => 'textarea',

Page 26: Comenzando con ZEND framework

Formularios 23

34 ),

35 'options' => array(

36 'label' => 'Texto de post',

37 ),

38 ));

39 $this->add(array(

40 'name' => 'submit',

41 'attributes' => array(

42 'type' => 'submit',

43 'value' => 'Enviar',

44 'id' => 'submitbutton',

45 ),

46 ));

47 }

48 }

https://gist.github.con/eminetto/0dfea8a3ef47061fb742²¹

El código es bastante autoexplicativo, aunque voy comentar algunos puntos importantes:

1 $this->setAttribute('method', 'post');

2 $this->setAttribute('action', '/post/save');

Estamos configurando el método de envio de los datos para el método POST e indicando la ACTIONdel formulario, es decir, para donde serán enviados los datos, es la url /post/save. Esto significa quenecesitamos también crear una nueva action en PostController, y eso haremos a continuación.

1 $this->add(array(

2 'name' => 'title',

3 'attributes' => array(

4 'type' => 'text',

5 ),

6 'options' => array(

7 'label' => 'Título',

8 ),

9 ));

En este trozo estamos creado un input HTML de tipo text y configurando el nombre y label. Para cadainput existe un componente en namespace Zend\Form\Element, como puede ser visto en manual deframework](http://framework.zend.con/manual/2.0/en/modules/zend.form.elements.html).

²¹https://gist.github.con/eminetto/0dfea8a3ef47061fb742

Page 27: Comenzando con ZEND framework

Formularios 24

Antes de crearnos la action que irá usar este formulario para crear y editar los posts necesitamospensar en un punto importante de cualquier aplicación: la validación de los campos. Podemoshacer esto de varias formas pero la más indicada es centralizar esta validación donde hacemos larepresentación de los datos, es decir, en la claseModel\Post. Vamos a modificar la clase para incluirel uso de los componentes de Zend\InputFilter. La versión actualizada de la clase se encuentra abajo:

1 <?php

2

3 namespace Application\Model;

4

5 use Zend\InputFilter\InputFilter;

6 use Zend\InputFilter\InputFilterAwareInterface;

7 use Zend\InputFilter\InputFilterInterface;

8

9 class Post implements InputFilterAwareInterface

10 {

11 public $id;

12 public $title;

13 public $description;

14 public $post_date;

15

16 //usado por TableGateway

17 public function exchangeArray($data)

18 {

19 $this->id = (!empty($data['id'])) ? $data['id'] : null;

20 $this->title = (!empty($data['title'])) ? $data['title'] : null;

21 $this->description = (!empty($data['description'])) ? $data['descripti\

22 on'] : null;

23 $this->post_date = (!empty($data['post_date'])) ? $data['post_date'] :\

24 null;

25 }

26

27 // Exigido por la implementación de la interfaz InputFilterAwareInterface

28 public function setInputFilter(InputFilterInterface $inputFilter)

29 {

30 throw new \Exception("No usado");

31 }

32

33 /**

34 * Configura los filtros de los campos de la clase

35 *

36 * @return Zend\InputFilter\InputFilter

37 */

Page 28: Comenzando con ZEND framework

Formularios 25

38 public function getInputFilter()

39 {

40 if (!$this->inputFilter) {

41 $inputFilter = new InputFilter();

42

43 $inputFilter->add(array(

44 'name' => 'id',

45 'required' => true,

46 'filters' => array(

47 array('name' => 'Int'),

48 ),

49 ));

50

51 $inputFilter->add(array(

52 'name' => 'title',

53 'required' => true,

54 'filters' => array(

55 array('name' => 'StripTags'),

56 array('name' => 'StringTrim'),

57 ),

58 'validators' => array(

59 array(

60 'name' => 'StringLength',

61 'options' => array(

62 'encoding' => 'UTF-8',

63 'min' => 1,

64 'max' => 100,

65 ),

66 ),

67 ),

68 ));

69

70 $inputFilter->add(array(

71 'name' => 'description',

72 'required' => true,

73 'filters' => array(

74 array('name' => 'StripTags'),

75 array('name' => 'StringTrim'),

76 ),

77 ));

78

79 $inputFilter->add(array(

Page 29: Comenzando con ZEND framework

Formularios 26

80 'name' => 'post_date',

81 'required' => false,

82 'filters' => array(

83 array('name' => 'StripTags'),

84 array('name' => 'StringTrim'),

85 ),

86 ));

87

88 $this->inputFilter = $inputFilter;

89 }

90

91 return $this->inputFilter;

92 }

93

94 //necesario para el uso de los forms

95 public function getArrayCopy()

96 {

97 return get_object_vars($this);

98 }

99 }

https://gist.github.con/eminetto/d0354d3992d704ea08b1²²

Algunos detalles sobre lo que podemos configurar:

• Si los campos son obligatorios o no, usando required con valor true o false (ejemplo: línea 45)• Los filtros. Ejemplos pueden ser los utilizados en el filtro Int (línea 47), que transforma el campoen entero; StripTags (línea 55) que elimina los tags HTML/JavaScript del texto; y StringTrim(línea 56) que elimina espacios al principio y final del texto. Existen otros filtros que puedenser consultado el manual de framework, además de la posibilidad de crearnos los nuestros.

• Las validaciones. Un ejemplo puede ser encontrado en StringLength (líneas 60 a 66) que validasi el campo posee a codificación de caracteres UTF-8 y si el tamaño esta entre 1 y 100. En el casoalgún de estos parametros no fuera respetado (por ejemplo intentado guardar 101 caracteres)sería generada una Exception indicando que el campo no es valido. Existen otros validadoresque pueden ser encontrados en el manual de framework, además de la posibilidad de crearnosotros.

Al centralizar la configuración de los filtros y validadores en la entidad podemos reutilizarlos enotros puntos de proyecto, como en formularios o hasta en el acceso via API.

Tambien incluimos el método getArrayCopy que es usado internamente por el formulario para hacero rellenado automático de los datos.

²²https://gist.github.con/eminetto/d0354d3992d704ea08b1

Page 30: Comenzando con ZEND framework

Formularios 27

Vamos ahora crear el action que hará uso de formulario y las configuraciones de validación. Vamosincluir el saveAction en PostController:

El código actualizado de module/Application/src/Application/Controller/PostController.php:

1 <?php

2 namespace Application\Controller;

3

4 use Zend\View\Model\ViewModel;

5 use Zend\Mvc\Controller\AbstractActionController;

6 use Application\Form\Post as PostForm;

7 use Application\Model\Post as PostModel;

8

9 /**

10 * Controlador que gestiona los posts

11 *

12 * @category Application

13 * @package Controller

14 * @author Elton Minetto <[email protected]>

15 */

16 class PostController extends AbstractActionController

17 {

18

19 private $tableGateway;

20

21 private function getTableGateway()

22 {

23 if (!$this->tableGateway) {

24 $this->tableGateway = $this->getServiceLocator()

25 ->get('Application\Model\PostTableGateway\

26 ');

27 }

28 return $this->tableGateway;

29 }

30

31 /**

32 * Muestra los posts guardados

33 * @return void

34 */

35 public function indexAction()

36 {

37 $tableGateway = $this->getTableGateway();

38 $posts = $tableGateway->fetchAll();

Page 31: Comenzando con ZEND framework

Formularios 28

39 return new ViewModel(array(

40 'posts' => $posts

41 ));

42 }

43

44 /**

45 * Crea o edita un post

46 * @return void

47 */

48 public function saveAction()

49 {

50 $form = new PostForm();

51 $tableGateway = $this->getTableGateway();

52 $request = $this->getRequest();

53 /* si la petición es POST los datos fueron recibidos via formulario*/

54 if ($request->isPost()) {

55 $post = new PostModel;

56 /* configura la validación de formulario con los filtros

57 y validators de la entidad*/

58 $form->setInputFilter($post->getInputFilter());

59 /* rellena o formulario con los datos que el usuario introdujo en la\

60 pantalla */

61 $form->setData($request->getPost());

62 /* realiza la validación de formulario*/

63 if ($form->isValid()) {

64 /* obtiene los datos validatos y filtrados */

65 $data = $form->getData();

66 /* guarda los datos de inserción del post*/

67 $data['post_date'] = date('Y-m-d H:i:s');

68 /* rellena los datos de objeto Post con los datos de formulario \

69 */

70 $post->exchangeArray($data);

71 /* guarda el nuevo post*/

72 $tableGateway->save($post);

73 /* redirecciona para la página inicial que muestra todos los pos\

74 ts */

75 return $this->redirect()->toUrl('/post');

76 }

77 }

78 /* esta es la forma de recuperar un parámetro recibido de una url como:

79 http://empezando-zf2.dev/post/save/1

80 */

Page 32: Comenzando con ZEND framework

Formularios 29

81 $id = (int) $this->params()->fromRoute('id', 0);

82 if ($id > 0) { //es una actualización

83 /* busca la entidad en la base de datos */

84 $post = $tableGateway->get($id);

85 /* rellena el formulario con los datos de la base de datos*/

86 $form->bind($post);

87 /* cambia el texto del botón submit*/

88 $form->get('submit')->setAttribute('value', 'Edit');

89 }

90 return new ViewModel(

91 array('form' => $form)

92 );

93 }

94

95 /**

96 * Elimina un post

97 * @return void

98 */

99 public function deleteAction()

100 {

101 $id = (int) $this->params()->fromRoute('id', 0);

102 if ($id == 0) {

103 throw new \Exception("Código obligatorio");

104 }

105 /* eliminar el registro y redirecciona para la página inicial */

106 $tableGateway = $this->getTableGateway()->delete($id);

107

108 return $this->redirect()->toUrl('/post');

109 }

110

111 }

https://gist.github.con/eminetto/62c62140933ad70e7481²³

En la nueva versión incluí también el método deleteAction que elimina el post y realice unarefactorización creado el método getTableGateway para dejar el código más legible. Este controladorahora posee nuevos e importantes conceptos. Para facilitar el entendimento de cada un de ellos incluícomentários explicando cada trozo de código. Recomiendo la lectura con atención de código anterior,mas un de los trozos más interesantes es $form->setInputFilter($post->getInputFilter()); pues usamosla configuración de validación de la capa de modelos directamente en el formulario, sin necesidadde reescribir nada. Esto ayuda mucho en el mantenimiento de las reglas de validación.

²³https://gist.github.con/eminetto/62c62140933ad70e7481

Page 33: Comenzando con ZEND framework

Formularios 30

El próximo paso es crear la vista para esta nueva action, en el archivomodule/Application/view/ap-plication/post/save.phtml:

1 <?php

2 echo $this->form()->openTag($form);

3 echo $this->formCollection($form);

4 echo $this->form()->closeTag();

Es lo que necesitamos para que el formulario se transforme en HTML. Podemos cambiar la formacomo el código HTML es generado modificando las configuraciones del objeto form. En el manualde framework²⁴ tenemos más detalles sobre esto.

Con esto terminamos la creación de nuestro CRUD (Create, Retrieve, Update, Delete) de la clase Post.Dejo como ejercicio para el lector hacer lo mismo para las clases Comment y User, conforme a lomostrado en los capítulos anteriores.

²⁴http://framework.zend.con/manual/2.0/en/index.html#zend-form

Page 34: Comenzando con ZEND framework

Próximos pasosEn este e-book apenas “arañando la superficie, vimos solo lo minimo que podemos hacer conZend Framework 2. A idea era mostrar lo básico y dejar al lector con ganas de profundizar losconocimientos en este vasto universo.

Como sugerencias de caminos a seguir dejo lo siguiente:

• Mi e-book “Zend Framework 2 en la prática” es una versión más completa y detallada, mos-trando como usar componentes avanzados como Cache, Traducciones, Paginador, Módulos,Eventos y Servicios. Usando este link²⁵ puedes comprar la versión digital con descuento.Tambien existe una versión impresa de libro, que puede ser comprada en este link²⁶. Existe uncurso em formato video basado en el contenido del libro Code Squad²⁷

• La guia de referencia de Zend Framework²⁸ es muy útil, principalmente cuando el desarrolla-dor ya sabe los componentes que va utilizar y necesita profundizar en detalle con ejemplos deuso.

• Otra herramienta que es bastante usada en conjunto con Zend Framework 2 es Doctrine, unORM que facilita el trabajo en la capa de modelo de las aplicaciones. Escribi un e-book sobreDoctrine donde presento de forma práctica el framework incluyendo como usarlo con ZendFramework 2. En este link²⁹ puedes comprar el e-book con descuento.

Espero que haya conseguido demostrar un poco del potencial de Zend Framework 2 y tambien miinteres en él. Llevo usando el framework, en conjunto con Doctrine, en varios proyectos desde sulanzamiento y la productividad es impresionante, así como la robustez de las soluciones creadas.

Para entrar en contacto conmigo lamaneramás facil esmediantemi sitio personal, en http://eltonminetto.net³⁰.Ahi tambien encontraras todos mis contactos, como e-mail, Twitter, etc.

Happy coding!

²⁵https://leanpub.com/zend-framework2-na-pratica/c/nNubG8uYV2Gu²⁶http://clubedeautores.com.br/book/141791--Zend_Framework_2_na_pratica#.VHEdqflfa-0²⁷http://code-squad.com/curso/zf2-na-pratica/avulso²⁸http://framework.zend.com/manual/2.3/en/index.html²⁹https://leanpub.com/doctrine-na-pratica/c/J5nYArCKrRq6³⁰http://eltonminetto.net