You are on page 1of 34

Comenzando con Zend Framework 2

eminetto y Antonio Garcia


Este libro est a la venta en http://leanpub.com/comenzando-zf2
Esta versin se public en 2014-12-29

This is a Leanpub book. Leanpub empowers authors and publishers with the Lean Publishing
process. Lean Publishing is the act of publishing an in-progress ebook using lightweight tools and
many iterations to get reader feedback, pivot until you have the right book and build traction once
you do.
2014 eminetto y Antonio Garcia

ndice general
Introducin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

Instalando . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Requisitos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Instalando el framework . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

2
2
3

Definiendo el proyecto . .
Descripicin . . . . . . .
Modelado . . . . . . . .
Creacin de las tablas . .
Configurando el proyecto

.
.
.
.
.

6
6
6
6
8

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

10
10

Controladores, rutas y vistas


Controladores . . . . . . .
Vistas . . . . . . . . . . .
Layouts . . . . . . . . . .
Rutas . . . . . . . . . . . .
Desafo . . . . . . . . . . .

.
.
.
.
.
.

14
14
15
17
20
21

Formularios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

22

Prximos pasos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

31

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.

Introducin
La idea de este libro es mostrar los primeros pasos en el mundo de Zend Framework 2. No vamos
a profundizar en tecnicas complejas para construir proyectos con Zend Framework 2, pero vamos a
ver como crear un CRUD bsico y entender algunos conceptos importantes.
En el ultimo capitulo del libro veremos cuales serian los proximos pasos a dar, para donde ir despus
de esta primera toma de contacto.
Empezamos?

Instalando
El proceso de instalacion de Zend Framework 2 fue uno de los asuntos que ms ha mejorado desde
versiones anteriores. La instalacin y actualizacin del framework y sus dependencias es realmente
muy fcil.

Requisitos
Para crear un proyecto que utilice Zend Framework 2 necesitamos cumplir los siguiente requisitos:
Un servidor Web. El ms utilizado es Apache aunque se pueden utilizar otros como _IIS o
Nginx. Los ejemplos de este libro se basan en el servidor Apache. PHP, a partir de la version
5.4, incluye un servidor web embebido, pero no consider usarlo para este libro puesto que no
todos los ambientes de desarrollo estan actualizados con las versiones ms actualizadas. En el
caso de utilizar Apache es necesario que el mdulo mod_rewrite est habilitado. En el fichero
de configuracin bastar aadir las lineas de abajo, o modificarlas para obtener lo siguiente:
1
2
3

LoadModule rewrite_module modules/mod_rewrite.so


AddModule mod_rewrite.c
AllowOverride all

Un sistema gestor base de datos. No es obligaorio pero en nuestro caso vamos utilizar MySQL.
Puedes utilizar otro gestor de base de datos como SQLite o PostgreSQL, aunque los ejemplos
estn escritos para MySQL.
PHP 5.3.3 o superior.
Extensin intl de PHP. El framework usa esta extensin para formatear fechas y numeros.
Esta extensin puede instalarse usando comando pecl de PHP.
Si estamos usando Windows o MacOSX estos requisitos pueden ser facilmente cumplidos instalando
un de los paquetes de desarrollo famosos como XAMPP (Windows y MacOSX ) o MAMP (MacOSX),
que incluyen todos los paquetes configurados.
Si utiliza Linux bastar usar el sistema de gestin de paquetes (apt-get, yum, etc) para instalar los
paquetes necesarios.
https://www.apachefriends.org/index.html
http://www.mamp.info/en/index.html

Instalando

Instalando el framework
La forma recomendada de crear un proyecto es usar uno de los de los esqueletos de aplicacion que
estan disponibles en Github. La documentacin 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 a
nuestro directorio de proyectos. En mi MacOSX ese directorio es /Users/eminetto/Documents/Projects/ . En tu caso puede ser cualquier otro directorio.
Ejecutamos los comandos siguientes:
1
2
3

cd /Users/eminetto/Documents/Projects/
git clone https://github.con/zendframework/ZendSkeletonApplication.git empezando\
-zf2

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


Si no tuvieras git instalado en tu mquina puedes descargar y descomprimir en el directorio. La
descarga 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 aplicacin nos tendriamos completamente
el framework. La forma ms rapida de tener el framework instalado completamente seria usando
la herramienta Composer. Esta herramienta fue creada para instalar y actualizar dependencias de
cdigo en proyectos PHP. Para entender con detalle como funciona Composer te recomiendo este
screencast y la web oficial de la herramienta. Nota del traductor: En espaol podriamos visitar
screencast de Desarrolloweb.con para una introduccin a Composer.
Vamos usar Composer para terminar de instalar el framework:
1
2
3

cd empezando-zf2
php composer.phar self-update
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

Instalando

El primer comando, self-update no es obligatorio pero es recomendable pues te garantiza que


Composer est actualizado con a ltima versin estable. Con el segundo comando Composer
realizar la descarga del framework y todas sus dependencias, adems de configurar un autoloader
que el framework usar.
Esto es mucho ms rpido y fcil que las versiones antiguas, que pedian registrarse en la web de
Zend.

Configurar Vhosts de Apache


Un hbito que tengo siempre que desarrollo un nuevo proyecto es crear un servidor virtual en mi
mquina para aislar el entorno del proyecto. Esto facilita bastante las pruebas, la organizacin de
los proyectos es hasta el mismo deploy de la aplicacin en el servidor de produccin al final del
desarrollo.
Para esto vamos configurar un servidor virtual en Apache. En el archivo httpd.conf (o apache.conf
o la configuracin de servidores virtuales de tu sistema operativo) aadir lo siguiente:
1
2
3
4
5
6
7
8
9
10
11
12

<VirtualHost *:80>
ServerName empezando-zf2.dev
DocumentRoot /Users/eminetto/Documents/Projects/empezando-zf2/public
SetEnv APPLICATION_ENV "development"
SetEnv PROJECT_ROOT "/Users/eminetto/Documents/Projects/empezando-zf2"
<Directory /Users/eminetto/Documents/Projects/empezando-zf2/public>
DirectoryIndex index.php
AllowOverride All
Order allow,deny
Allow from all
</Directory>
</VirtualHost>

https://gist.github.con/eminetto/318b15ff2554a1db58cd
Es necesario modificar las rutas en las opciones DocumentRoot, PROJECT_ROOT y Directory con
la ruta correcta en su mquina. Tambien modificaremos el archivo hosts de sistema operativo para
aadir la direccin iniciando-zf2.dev puesto que no existe ningn DNS.
En Linux y Mac OSX, modificamos /etc/hosts aadiendo la lnea:
1

127.0.0.1

empezando-zf2.dev

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

Instalando

En Windows el archivo que debe ser modificador es c:\windows\system32\drivers\etc\hosts y la lnea


para aadir es la misma para los otros sistemas operativos.
Ahora podemos realizar una prueba para verificar si nuestro esqueleto de proyecto est funcionando
accediendo a la direccin: http://empezando-zf2.dev em algn navegador. La pantalla esperada es la
mostrada ms abajo.

Welcome

Esta es la pantalla de bienvenida de Zend Framework 2. En el caso de que esta pantalla no este
apareciendo recomiendo repasar los pasos anteriores y tambien comprobar los logs de Apache
para identificar que puede estar incorrecto. Normalmente la configuracin del virtual host es el
la causante de los fallos.
Con estos pasos tenemos el entorno instalado y podemos iniciar la planificacin de nuestro primer
proyecto utilizando Zend Framework 2.

Definiendo el proyecto
Descripicin
En mi opinin la mejor forma de aprender una nueva herramienta, lenguaje o sistema operativo es
cuando necesitas realmente resolver algn problema con ella. Pensando en eso, este libro esta basado
en la construccin de una aplicacin: un blog.
Porque un blog? Por algunos motivos:
Es un problema fcil de entender. Todo mundo sabe como funciona un blog, sus requisitos y
funcionalidades, y as la fase de toma de requisitos del proyecto es fcil de completar.
Un blog posee un gran nmero de funcionalidades comunes con otras aplicaciones, como
mdulos, control de acesso y permisos, subida de archivos, tratamento de formularios, cache,
traduciones, integracin con servicios externos, etc.
La gran mayoria de los frameworks poseen un ejemplo como desarrollar un blog usando
X, as resulta ms fcil comparar si ya conocias algn 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.

Creacin de las tablas


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

Definiendo el proyecto

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

create database empezandozf2;


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

TABLE IF NOT EXISTS `users` (


`id` INT NOT NULL AUTO_INCREMENT ,
`username` VARCHAR(200) NOT NULL ,
`password` VARCHAR(250) NOT NULL ,
`name` VARCHAR(200) NULL ,
`valid` TINYINT NULL ,
`role` VARCHAR(20) NULL ,
PRIMARY KEY (`id`) )
ENGINE = InnoDB;
CREATE

TABLE IF NOT EXISTS `posts` (


`id` INT NOT NULL AUTO_INCREMENT ,
`title` VARCHAR(250) NOT NULL ,
`description` TEXT NOT NULL ,
`post_date` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ,
PRIMARY KEY (`id`) )
ENGINE = InnoDB;
CREATE

TABLE IF NOT EXISTS `comments` (


`id` INT NOT NULL AUTO_INCREMENT ,
`post_id` INT NOT NULL ,
`description` TEXT NOT NULL ,
`name` VARCHAR(200) NOT NULL ,
`email` VARCHAR(250) NOT NULL ,
`webpage` VARCHAR(200) NOT NULL ,
`comment_date` TIMESTAMP NULL ,
PRIMARY KEY (`id`, `post_id`) ,
INDEX `fk_comments_posts` (`post_id` ASC) ,
CONSTRAINT `fk_comments_posts`
FOREIGN KEY (`post_id` )
REFERENCES `posts` (`id` )
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;

Definiendo el proyecto

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

Configurando el proyecto
Zend Framework 2 cuenta con archivos de configuracin separados que son unificados en el
momento de ejecucin.
Los principales archivos que vamos usar durante el proyecto son:
config/application.config.php: Archivo con las configuraciones generales de la aplicacin. Son
configuraciones usadas por todos los mdulos y componentes.
config/autoload/global.php y config/autoload/local.php: El archivo global.php es usado como
complemento de application.config.php pues tambin contiene configuraciones de la aplicacin y funcionan como un todo. La idea es colocar en este archivo configuraciones que pueden
cambiar de acuerdo con la mquina del desarrollador. Un ejemplo son las configuraciones de
conexin con la base de datos. Estas configuraciones pueden ser modificados para las mquinas locales, de los desarrolladores. Para eso el desarrollador sobreescribe las configuraciones
en local.php. El archivo local.php no debe ser guardado en el control de versiones (svn o git
por ejemplo).
_module/
Los archivos de configuracin son generalmente scripts PHP que devuelven arrays de configuracin.
Son rpidos durante la ejecucin y de fcil lectura.

global.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

<?php
return array(
'service_manager' => array(
'factories' => array(
'Zend\Db\Adapter\Adapter' => 'Zend\Db\Adapter\AdapterServiceFactory',
),
),
'db' => array(
'driver'
=> 'Pdo',
'dsn'
=> 'mysql:dbname=empezandozf2;host=localhost',
'driver_options' => array(
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\''
),
)
);
https://gist.github.con/eminetto/284eddfbc1370c113ee2

Definiendo el proyecto

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

local.php
1
2
3
4
5
6
7

<?php
return array(
'db' => array(
'username' => 'zend',
'password' => 'zend',
)
);

https://gist.github.con/4011983
Volvermos a ver estos archivos de configuracin en el transcurso del proyecto, y sus elementos pasaran a tener ms 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

Modelos
Vamos a comenzar el desarrollo por la primera capa de nuestra aplicacin MVC: los modelos. Para
esto vamos crear un archivo para cada tabla y estos deben ser guardados en el directorio Model de
src de mdulo, 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

<?php
namespace Application\Model;
class Post
{
public
public
public
public

$id;
$title;
$description;
$post_date;

//usado por TableGateway


public function exchangeArray($data)
{
$this->id
= (!empty($data['id'])) ? $data['id'] : null;
$this->title = (!empty($data['title'])) ? $data['title'] : null;
$this->description = (!empty($data['description'])) ? $data['descripti\
on'] : null;
$this->post_date = (!empty($data['post_date'])) ? $data['post_date'] :\
null;
}
}

Modelos

11

https://gist.github.con/eminetto/a38dc854af21e32447e4
El primer punto importante a ser destacado es el uso del concepto de Namespaces. Zend Framework 2
usa extensivamente el concepto de Namespaces que fue introducido en PHP 5.3. En la primera lnea
indicamos cual espacio donde el cdigo que vamos escribir pertenece, siendo obligatorio siempre
utilizar uno. En las prximas lneas podemos indicar los componentes que vamos a nacesitar y a que
namespaces pertenecen usando el comando use (vamos ver esto en las prximas pginas). Haciendo
esto las clases son automticamente cargadas, sin necesidad de preocuparnos en usar include o
require, dejando esta tarea para el mecanismo de autoloading implementado por el framework. Ms
informacin sobre namespaces pueden ser consultadas en el manual de PHP.
La clase Post es simplemente una representacin de los datos de la base de datos, pero necesita ser
manipulada por alguna otra clase. Podemos usar dos herramientas para hacer esa manipulacin: un
TableGateway o un ORM como Doctrine. A funcin de un TableGateway es realizar operaciones
sobre entidades pues ellas no poseen comportamiento. Las principales operaciones como insertar,
eliminar, actualizar, buscar sern siempre realizadas atravs de este gateway. Existen muchas
ventajas de esta aproximacin como poder cambiar la capa de entidades (substituir por entidades de
ORM Doctrine por ejemplo, que no vamos ver en este libro) o cambiar el comportamiento de todas las
entidades simplemente alterando los gateways. Vamos ahora crear un TableGateway para manipular
la clase Post con el archivo module/Application/src/Application/Model/PostTableGateway.php:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

<?php
namespace Application\Model;
use Zend\Db\TableGateway\TableGateway;
class PostTableGateway
{
protected $tableGateway;
public function __construct(TableGateway $tableGateway)
{
$this->tableGateway = $tableGateway;
}
public function fetchAll()
{
$resultSet = $this->tableGateway->select();
return $resultSet;
}
https://gist.github.con/eminetto/a38dc854af21e32447e4
http://php.net/manual/es/language.namespaces.php

12

Modelos

21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56

public function get($id)


{
$id = (int) $id;
$rowset = $this->tableGateway->select(array('id' => $id));
$row = $rowset->current();
if (!$row) {
throw new \Exception("No encontrado id $id");
}
return $row;
}
public function save(Post $post)
{
$data = array(
'title' => $post->title,
'description' => $post->description,
'post_date' => $post->post_date,
);
$id = (int) $post->id;
if ($id == 0) {
$this->tableGateway->insert($data);
} else {
if ($this->get($id)) {
$this->tableGateway->update($data, array('id' => $id));
} else {
throw new \Exception('Post no existe');
}
}
}
public function delete($id)
{
$this->tableGateway->delete(array('id' => (int) $id));
}
}

https://gist.github.con/eminetto/0d5e1e5b11a53523ee36
Como podemos ver en el cdigo anterior, la clase PostTableGateway necesita de una instancia de
la clase TableGateway para ser creada, podemos decir que esta clase posee una dependencia. A
su vez la clase TableGateway tambin posee una srie de dependencias, lo que comienza a tornar
https://gist.github.con/eminetto/0d5e1e5b11a53523ee36

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 y
el se encarga de crearlas par nosotros, siempre que necesitemos. En el archivo de configuraciones
de nuestro mdulo vamos incluir estas nuevas reglas de dependencias, modificando el archivo
module/Application/config/module.config.php:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

'service_manager' => array(


'abstract_factories' => array(
'Zend\Cache\Service\StorageCacheAbstractServiceFactory',
'Zend\Log\LoggerAbstractServiceFactory',
),
'aliases' => array(
'translator' => 'MvcTranslator',
),
'factories' => array(
'Application\Model\PostTableGateway' => function($sm) {
$dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
$resultSetPrototype = new Zend\Db\ResultSet\ResultSet();
$resultSetPrototype->setArrayObjectPrototype(new Application\Mod\
el\Post());
$tableGateway = new Zend\Db\TableGateway\TableGateway('posts, $\
dbAdapter, null, $resultSetPrototype);
$postTableGateway = new Application\Model\PostTableGateway($tabl\
eGateway);
return $postTableGateway;
},
),
),

https://gist.github.con/eminetto/b6487feb6f0de85ada38
Lo que estamos diciendo al framework es: siempre que solicitemos el servicio Application\Model\PostTableGateway
ejecute esta funcin annima con todos los pasos necesarios para su creacin y me devuelva una
instancia lista para funcionar. En el prximo captulo vamos ver como usar el ServiceManager para
que nos devuelva la instancia de PostTableGateway configurada y lista para su uso.
Voy dejar a configuracin de las otras tablas de la base de datos como ejercicio para el lector y vamos
ahora trabajar con la prxima capa, los controladores.
https://gist.github.con/eminetto/b6487feb6f0de85ada38

Controladores, rutas y vistas


Controladores
Ls controladores son responsables de la interaccin con el usuario, interceptando las invocaciones
por urls, clicks en links, etc. El controlador recibir la accin del usuario, ejecutar algn servicio,
manipular alguna clase de la capa de modelo y, generalmente, renderizar una pantalla de la capa de
la 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 nuestro
uso, como AbstractActionController y AbstractRestfulController.
Vamos a crear el controlador, en el archivo module/Application/src/Application/Controller/PostController.php:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

<?php
namespace Application\Controller;
use Zend\View\Model\ViewModel;
use Zend\Mvc\Controller\AbstractActionController;
/**
* Controlador que gestiona los posts
*
* @category Application
* @package Controller
* @author Elton Minetto <eminetto@coderockr.con>
*/
class PostController extends AbstractActionController
{
/**
* Muestra los posts guardados
* @return void
*/
public function indexAction()
{

Controladores, rutas y vistas

22
23
24
25
26
27
28
29

15

$tableGateway = $this->getServiceLocator()->get('Application\Model\PostTabl\
eGateway');
$posts = $tableGateway->fetchAll();
return new ViewModel(array(
'posts' => $posts
));
}
}

https://gist.github.con/eminetto/86e810dac94a3f761d73
Podemos ver ahora el uso del ServiceManager para obtener una instancia de PostTableGateway,
conforme a la configuracin del captulo anterior y al uso del mtodo fetchAll() que devuelve todos
los registros de la tabla. indexAction crea una instancia de la clase ViewModel y la configura con
una variable llamada posts con el resultado de la consulta de la base de datos. Al devolver una
instancia de ViewModel estamos solicitando al framework que renderize el HTML de la vista. Como
no indicamos alguna vista especfica se renderizara la vista por defecto. Como estamos accediendo a
la action index del controlador PostController el framework buscar un archivo llamado index.phtml
en el directorio: module/Application/view/application/post/.

Vistas
A pesar de la extensin diferente (.phtml) la vista no deja de ser un archivo PHP donde podemos
usar todas las funciones nativas del lenguaje y algunos componentes especiales,llamados de View
Helpers.
El contenido de archivo index.phtml sera:
1
2
3
4
5
6
7
8
9
10
11
12
13

<div class="actions clearfix">


<div class="btns">
<a class="btn btn-primary" href="/post/save" title="Crear Post">
Crear Post
</a>
</div>
</div>
<h3>Lista de Posts</h3>
<table class="table table-striped table-bordered">
<thead>
<tr>
<th>Ttulo</th>
<th>Texto</th>
https://gist.github.con/eminetto/86e810dac94a3f761d73

Controladores, rutas y vistas

14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

16

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


<th width="120" class="center">Opciones</th>
</tr>
</thead>
<tbody>
<?php foreach($posts as $post):?>
<tr>
<td><?php echo $this->escapeHtml($post->title);?></td>
<td><?php echo $this->escapeHtml($post->description);?></td>
<td class="center">
<?php
echo $this->dateFormat(
\DateTime::createFromFormat('Y-m-d H:i:s', $post->post_date),
\IntlDateFormatter::SHORT,
\IntlDateFormatter::SHORT,
'es'
);
?>
</td>
<td class="center">
<a href="/post/save/<?php echo $post->id ;?>"
title="Editar">
Editar
</a>
<a href="/post/delete/<?php echo $post->id;?>"
rel="confirmation"
title="Desea eliminar este registro?">
Eliminar
</a>
</td>
</tr>
<?php endforeach;?>
</tbody>
</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 registros
obtenidos de la tabla post.
https://gist.github.con/eminetto/219a41d5be98784c6f89

17

Controladores, rutas y vistas

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

Este es un ejemplo del uso de un ViewHelper, escapeHtml, que limpia cualesquiera tags HTML que
puedan 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 de
layouts, que son estructuras o pginas base que fusionamos con las vistas de los controladores, como
a index.phtml.
Usando una imagem para ilustrar un ejemplo:

Wireframe mostrando un layout

En todas las pginas tendremos un ttulo, una imagen y un pie, con informacin sobre el autor,
copyright, etc. La nica informaccin que cambiar es el contenido de las pginas, como index.phtml
que acabamos de crear, pero la cabecera y el pie permaneceran sin cambios.
Podemos tener varios layouts y usaremos el ms indicado en cada controlador o action. El cdigo
del layout por defecto de Zend Framework 2 se encuentra en el archivo module/Application/view/layout/layout.phtml:

18

Controladores, rutas y vistas

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

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


<html lang="en">
<head>
<meta charset="utf-8">
<?php echo $this->headTitle('ZF2 '. $this->translate('Skeleton Applicati\
on'))->setSeparator(' - ')->setAutoEscape(false) ?>
<?php echo $this->headMeta()
->appendName('viewport', 'width=device-width, initial-scale=1.0')
->appendHttpEquiv('X-UA-Compatible', 'IE=edge')
?>
<!-- Le styles -->
<?php echo $this->headLink(array('rel' => 'shortcut icon', 'type' => 'im\
age/vnd.microsoft.icon', 'href' => $this->basePath() . '/img/favicon.ico'))
->prependStylesheet($this->basePath() . '/css/style.css')
->prependStylesheet($this->basePath() . '/css/bootstrap-\
theme.min.css')
->prependStylesheet($this->basePath() . '/css/bootstrap.\
min.css') ?>
<!-- Scripts -->
<?php echo $this->headScript()
->prependFile($this->basePath()
->prependFile($this->basePath()
->prependFile($this->basePath()
ript', array('conditional' => 'lt IE 9',))
->prependFile($this->basePath()
ript', array('conditional' => 'lt IE 9',))
; ?>

. '/js/bootstrap.min.js')
. '/js/jquery.min.js')
. '/js/respond.min.js', 'text/javasc\
. '/js/html5shiv.js',

'text/javasc\

</head>
<body>
<nav class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="col\
lapse" data-target=".navbar-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>

Controladores, rutas y vistas

43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66

19

</button>
<a class="navbar-brand" href="<?php echo $this->url('home') \
?>"><img src="<?php echo $this->basePath('img/zf2-logo.png') ?>" alt="Zend Frame\
work 2"/>&nbsp;<?php echo $this->translate('Skeleton Application') ?></a>
</div>
<div class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li class="active"><a href="<?php echo $this->url('home'\
) ?>"><?php echo $this->translate('Home') ?></a></li>
</ul>
</div><!--/.nav-collapse -->
</div>
</nav>
<div class="container">
<?php echo $this->content; ?>
<hr>
<footer>
<p>&copy; 2005 - <?php echo date('Y') ?> by Zend Technologies Lt\
d. <?php echo $this->translate('All rights reserved.') ?></p>
</footer>
</div> <!-- /container -->
<?php echo $this->inlineScript() ?>
</body>
</html>

https://gist.github.con/eminetto/3f5f0594afd63ec449c2
Este archivo hace uso de varios ViewHelpers que no veremos ahora, como headLink o translate, y
posee una estrutura ms compleja de diseo y CSS. La parte ms importante para nosotros ahora es
la lnea:
1

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

Este es el punto donde vista ser renderizada en el layout, es decir, el contenido de index.phtml
sera mezclado con el restante. Como todos los controladores usan este layout, por defecto este
comportamiento ser igual em todos, a menos que el desarrollador modifique la configuracin
de algn de ellos. El layout puede ser mucho mas simple, apenas necesitas constar esa lnea, que
imprime $this->content.
https://gist.github.con/eminetto/3f5f0594afd63ec449c2

Controladores, rutas y vistas

20

Rutas
Por motivos de seguridad y organizacin los controladores que creamos no son automaticamente
accesibles por los navegadores de los usuarios. Necesitamos primeramente habilitar el controlador
en el mdulo y despues crear una configuracin de enrutado, la URL que usuario usara para acceder.
Primero vamos habilitar el controlador en el archivo de configuraciones de nuestro mdulo, el
module/Application/config/module.config.php, en la opcin controllers :
1
2
3
4
5
6
7
8

'controllers' => array(


'invokables' => array(
'Application\Controller\Index' => 'Application\Controller\IndexContr\
oller',
'Application\Controller\Post' => 'Application\Controller\PostControl\
ler'
),
),

El prximo paso ser crear la ruta.


Abrimos un pequeo parntesis aqui, y comentar sobre como los controladores y actions funcionan.
Generalmente los controladores son clases cuyo nombre termina en Controller como en PostController (a pesar de esto no es obligatorio en Zend Framework 2 aunque continua siendo estndar).
Cada controlador posee una o ms actions que son mtodos pblicos cuyo nombre termina en Action
como en indexAction. Las actions son las acciones que los usuarios pueden acceder via URL, links o
botones 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 estndar:


1

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

Entonces:

Servidor = empezando-zf2.dev
Mdulo = Admin
Controller = IndexController.php
Action = indexAction (dentro de archivo IndexController.php)
Parmetro = id
Valor = 1

Controladores, rutas y vistas

21

Este es el comportamiento por defecto esperado por el framework pero nosotros podemos crear
nuestras rutas que mejor se adapten a nuestro proyecto. Vamos a crear las siguientes rutas para
nuestro pequeo proyecto:

/post: ser la lista de posts. La lgica estar en indexAction


/post/save: ser insercin de posts. La lgica estar en saveAction
/post/save/id/NUMERO: ser la edicin de posts. La lgica tambin estar en saveAction
/post/delete/id/NUMERO: ser la eliminacin de posts. La lgica estar en deleteAction

Para eso vamos crear una nueva clave en el array router de archivo module/Application/config/module.config.php, despues de la clave application:
1
2
3
4
5
6
7
8
9
10
11
12
13
14

'post' => array(


'type'
=> 'segment',
'options' => array(
'route'
=> '/post[/][:action][/:id]',
'constraints' => array(
'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
'id'
=> '[0-9]+',
),
'defaults' => array(
'controller' => 'Application\Controller\Post',
'action'
=> 'index',
),
),
),

https://gist.github.con/eminetto/ce1ce0286d6450ac5116
Ahora podemos acceder a la URL http://empezando-zf2.dev/post y visualizariamos los posts guardados en la base de datos. Los botones an no funcionan pues no creamos las nuevas actions. Haremos
esto en el prximo captulo.

Desafo
En este captulo vimos el controlador, las vistas y las rutas para manipular la entidad Post. Como
desafo dejo al lector aadir una nueva funcionalidad: el listado de comentarios de un post.
https://gist.github.con/eminetto/ce1ce0286d6450ac5116

Formularios
Zend Framework 2 posee un componente para ayudar en la creacin de los formularios, Zend\Form.
Vamos a crear nuestro primer formulario, module/Application/src/Application/Form/Post.php :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

<?php
namespace Application\Form;
use Zend\Form\Form;
class Post extends Form
{
public function __construct()
{
parent::__construct('post');
$this->setAttribute('method', 'post');
$this->setAttribute('action', '/post/save');
$this->add(array(
'name' => 'id',
'attributes' => array(
'type' => 'hidden',
),
));
$this->add(array(
'name' => 'title',
'attributes' => array(
'type' => 'text',
),
'options' => array(
'label' => 'Ttulo',
),
));
$this->add(array(
'name' => 'description',
'attributes' => array(
'type' => 'textarea',

23

Formularios

34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

),
'options' => array(
'label' => 'Texto de post',
),
));
$this->add(array(
'name' => 'submit',
'attributes' => array(
'type' => 'submit',
'value' => 'Enviar',
'id' => 'submitbutton',
),
));
}
}

https://gist.github.con/eminetto/0dfea8a3ef47061fb742
El cdigo es bastante autoexplicativo, aunque voy comentar algunos puntos importantes:
1
2

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

Estamos configurando el mtodo de envio de los datos para el mtodo POST e indicando la ACTION
del formulario, es decir, para donde sern enviados los datos, es la url /post/save. Esto significa que
necesitamos tambin crear una nueva action en PostController, y eso haremos a continuacin.
1
2
3
4
5
6
7
8
9

$this->add(array(
'name' => 'title',
'attributes' => array(
'type' => 'text',
),
'options' => array(
'label' => 'Ttulo',
),
));

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

24

Formularios

Antes de crearnos la action que ir usar este formulario para crear y editar los posts necesitamos
pensar en un punto importante de cualquier aplicacin: la validacin de los campos. Podemos
hacer esto de varias formas pero la ms indicada es centralizar esta validacin donde hacemos la
representacin de los datos, es decir, en la clase Model\Post. Vamos a modificar la clase para incluir
el uso de los componentes de Zend\InputFilter. La versin actualizada de la clase se encuentra abajo:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

<?php
namespace Application\Model;
use Zend\InputFilter\InputFilter;
use Zend\InputFilter\InputFilterAwareInterface;
use Zend\InputFilter\InputFilterInterface;
class Post
{
public
public
public
public

implements InputFilterAwareInterface
$id;
$title;
$description;
$post_date;

//usado por TableGateway


public function exchangeArray($data)
{
$this->id
= (!empty($data['id'])) ? $data['id'] : null;
$this->title = (!empty($data['title'])) ? $data['title'] : null;
$this->description = (!empty($data['description'])) ? $data['descripti\
on'] : null;
$this->post_date = (!empty($data['post_date'])) ? $data['post_date'] :\
null;
}
// Exigido por la implementacin de la interfaz InputFilterAwareInterface
public function setInputFilter(InputFilterInterface $inputFilter)
{
throw new \Exception("No usado");
}
/**
* Configura los filtros de los campos de la clase
*
* @return Zend\InputFilter\InputFilter
*/

25

Formularios

38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79

public function getInputFilter()


{
if (!$this->inputFilter) {
$inputFilter = new InputFilter();
$inputFilter->add(array(
'name'
=> 'id',
'required' => true,
'filters' => array(
array('name' => 'Int'),
),
));
$inputFilter->add(array(
'name'
=> 'title',
'required' => true,
'filters' => array(
array('name' => 'StripTags'),
array('name' => 'StringTrim'),
),
'validators' => array(
array(
'name'
=> 'StringLength',
'options' => array(
'encoding' => 'UTF-8',
'min'
=> 1,
'max'
=> 100,
),
),
),
));
$inputFilter->add(array(
'name'
=> 'description',
'required' => true,
'filters' => array(
array('name' => 'StripTags'),
array('name' => 'StringTrim'),
),
));
$inputFilter->add(array(

26

Formularios

80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99

'name'
=> 'post_date',
'required' => false,
'filters' => array(
array('name' => 'StripTags'),
array('name' => 'StringTrim'),
),
));
$this->inputFilter = $inputFilter;
}
return $this->inputFilter;
}
//necesario para el uso de los forms
public function getArrayCopy()
{
return get_object_vars($this);
}
}

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: lnea 45)
Los filtros. Ejemplos pueden ser los utilizados en el filtro Int (lnea 47), que transforma el campo
en entero; StripTags (lnea 55) que elimina los tags HTML/JavaScript del texto; y StringTrim
(lnea 56) que elimina espacios al principio y final del texto. Existen otros filtros que pueden
ser consultado el manual de framework, adems de la posibilidad de crearnos los nuestros.
Las validaciones. Un ejemplo puede ser encontrado en StringLength (lneas 60 a 66) que valida
si el campo posee a codificacin de caracteres UTF-8 y si el tamao esta entre 1 y 100. En el caso
algn de estos parametros no fuera respetado (por ejemplo intentado guardar 101 caracteres)
sera generada una Exception indicando que el campo no es valido. Existen otros validadores
que pueden ser encontrados en el manual de framework, adems de la posibilidad de crearnos
otros.
Al centralizar la configuracin de los filtros y validadores en la entidad podemos reutilizarlos en
otros puntos de proyecto, como en formularios o hasta en el acceso via API.
Tambien incluimos el mtodo getArrayCopy que es usado internamente por el formulario para hacer
o rellenado automtico de los datos.
https://gist.github.con/eminetto/d0354d3992d704ea08b1

Formularios

27

Vamos ahora crear el action que har uso de formulario y las configuraciones de validacin. Vamos
incluir el saveAction en PostController:
El cdigo actualizado de module/Application/src/Application/Controller/PostController.php:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

<?php
namespace Application\Controller;
use
use
use
use

Zend\View\Model\ViewModel;
Zend\Mvc\Controller\AbstractActionController;
Application\Form\Post as PostForm;
Application\Model\Post as PostModel;

/**
* Controlador que gestiona los posts
*
* @category Application
* @package Controller
* @author Elton Minetto <eminetto@coderockr.con>
*/
class PostController extends AbstractActionController
{
private $tableGateway;
private function getTableGateway()
{
if (!$this->tableGateway) {
$this->tableGateway = $this->getServiceLocator()
->get('Application\Model\PostTableGateway\
');
}
return $this->tableGateway;
}
/**
* Muestra los posts guardados
* @return void
*/
public function indexAction()
{
$tableGateway = $this->getTableGateway();
$posts = $tableGateway->fetchAll();

Formularios

39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80

return new ViewModel(array(


'posts' => $posts
));
}
/**
* Crea o edita un post
* @return void
*/
public function saveAction()
{
$form = new PostForm();
$tableGateway = $this->getTableGateway();
$request = $this->getRequest();
/* si la peticin es POST los datos fueron recibidos via formulario*/
if ($request->isPost()) {
$post = new PostModel;
/* configura la validacin de formulario con los filtros
y validators de la entidad*/
$form->setInputFilter($post->getInputFilter());
/* rellena o formulario con los datos que el usuario introdujo en la\
pantalla */
$form->setData($request->getPost());
/* realiza la validacin de formulario*/
if ($form->isValid()) {
/* obtiene los datos validatos y filtrados */
$data = $form->getData();
/* guarda los datos de insercin del post*/
$data['post_date'] = date('Y-m-d H:i:s');
/* rellena los datos de objeto Post con los datos de formulario \
*/
$post->exchangeArray($data);
/* guarda el nuevo post*/
$tableGateway->save($post);
/* redirecciona para la pgina inicial que muestra todos los pos\
ts */
return $this->redirect()->toUrl('/post');
}
}
/* esta es la forma de recuperar un parmetro recibido de una url como:
http://empezando-zf2.dev/post/save/1
*/

28

Formularios

81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111

29

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


if ($id > 0) { //es una actualizacin
/* busca la entidad en la base de datos */
$post = $tableGateway->get($id);
/* rellena el formulario con los datos de la base de datos*/
$form->bind($post);
/* cambia el texto del botn submit*/
$form->get('submit')->setAttribute('value', 'Edit');
}
return new ViewModel(
array('form' => $form)
);
}
/**
* Elimina un post
* @return void
*/
public function deleteAction()
{
$id = (int) $this->params()->fromRoute('id', 0);
if ($id == 0) {
throw new \Exception("Cdigo obligatorio");
}
/* eliminar el registro y redirecciona para la pgina inicial */
$tableGateway = $this->getTableGateway()->delete($id);
return $this->redirect()->toUrl('/post');
}
}

https://gist.github.con/eminetto/62c62140933ad70e7481
En la nueva versin inclu tambin el mtodo deleteAction que elimina el post y realice una
refactorizacin creado el mtodo getTableGateway para dejar el cdigo ms legible. Este controlador
ahora posee nuevos e importantes conceptos. Para facilitar el entendimento de cada un de ellos inclu
comentrios explicando cada trozo de cdigo. Recomiendo la lectura con atencin de cdigo anterior,
mas un de los trozos ms interesantes es $form->setInputFilter($post->getInputFilter()); pues usamos
la configuracin de validacin de la capa de modelos directamente en el formulario, sin necesidad
de reescribir nada. Esto ayuda mucho en el mantenimiento de las reglas de validacin.
https://gist.github.con/eminetto/62c62140933ad70e7481

Formularios

30

El prximo paso es crear la vista para esta nueva action, en el archivo module/Application/view/application/post/save.phtml:
1
2
3
4

<?php
echo $this->form()->openTag($form);
echo $this->formCollection($form);
echo $this->form()->closeTag();

Es lo que necesitamos para que el formulario se transforme en HTML. Podemos cambiar la forma
como el cdigo HTML es generado modificando las configuraciones del objeto form. En el manual
de framework tenemos ms detalles sobre esto.
Con esto terminamos la creacin 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 lo
mostrado en los captulos anteriores.
http://framework.zend.con/manual/2.0/en/index.html#zend-form

Prximos pasos
En este e-book apenas araando la superficie, vimos solo lo minimo que podemos hacer con
Zend Framework 2. A idea era mostrar lo bsico y dejar al lector con ganas de profundizar los
conocimientos en este vasto universo.
Como sugerencias de caminos a seguir dejo lo siguiente:
Mi e-book Zend Framework 2 en la prtica es una versin ms completa y detallada, mostrando como usar componentes avanzados como Cache, Traducciones, Paginador, Mdulos,
Eventos y Servicios. Usando este link puedes comprar la versin digital con descuento.
Tambien existe una versin impresa de libro, que puede ser comprada en este link. Existe un
curso em formato video basado en el contenido del libro Code Squad
La guia de referencia de Zend Framework es muy til, principalmente cuando el desarrollador ya sabe los componentes que va utilizar y necesita profundizar en detalle con ejemplos de
uso.
Otra herramienta que es bastante usada en conjunto con Zend Framework 2 es Doctrine, un
ORM que facilita el trabajo en la capa de modelo de las aplicaciones. Escribi un e-book sobre
Doctrine donde presento de forma prctica el framework incluyendo como usarlo con Zend
Framework 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 mi
interes en l. Llevo usando el framework, en conjunto con Doctrine, en varios proyectos desde su
lanzamiento y la productividad es impresionante, as como la robustez de las soluciones creadas.
Para entrar en contacto conmigo la manera ms facil es mediante mi 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

You might also like