0% encontró este documento útil (0 votos)
142 vistas24 páginas

Cake PHP

Este documento proporciona una introducción al framework PHP CakePHP. Explica cómo instalar CakePHP usando Composer, incluyendo los requisitos de PHP e instalación en diferentes sistemas operativos. También describe la estructura básica de directorios de un proyecto CakePHP y cómo acceder a una aplicación CakePHP recién instalada desde un navegador.
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
142 vistas24 páginas

Cake PHP

Este documento proporciona una introducción al framework PHP CakePHP. Explica cómo instalar CakePHP usando Composer, incluyendo los requisitos de PHP e instalación en diferentes sistemas operativos. También describe la estructura básica de directorios de un proyecto CakePHP y cómo acceder a una aplicación CakePHP recién instalada desde un navegador.
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
Está en la página 1/ 24

7

Curso PHP Medio Escuela de Administración Pública

CakePHP 3
7.1. Introducción
CakePHP es un marco de trabajo construido en PHP, con acceso a varios motores de bases de datos relacionales,
que implementa el patrón MVC y permite desarrollar de forma rápida y profesional proyectos web de cualquier
nivel de complejidad. Además, es libre, por tanto puede acceder a su código, aprender de él, modificarlo y difundir
esos cambios.
Es importante dejar claro que CakePHP no hace automáticamente la aplicación que queramos desarrollar por
nosotros. Más bien pone a su disposición una estructura básica sobre la que construir aplicaciones.
Entre sus características podemos destacar que utiliza convención sobre configuración, es decir, se determinan una
serie de normas a seguir por el desarrollador en lugar de cargar con múltiples parámetros la configuración de las
aplicaciones; dispone de multitud de plugins, REST, internacionalización, gestión de rutas, listas de control de
acceso, gestión de la seguridad, ayudantes para crear código HTML y formularios, uso de javascript, gestión de
sesiones y una larga lista más de características.
En la página web de CakePHP, http://www.cakephp.org, tiene un manual extenso, detallado y fácil de seguir en
multitud de idiomas.
Va a conocer un poco más de este framework instalando su última versión estable y creando un ejemplo simple.
No va a utilizar Bootstrap ni jQuery en este capítulo por razones de brevedad y para centrar el foco en CakePHP.
Tenga en cuenta que esta última versión, como requisito a destacar, debe tener instalada como mínimo la versión
5.6.0 de PHP o cualquier versión superior, como es nuestro caso con la versión 7.0.

7.2. Instalación
La forma mas sencilla de instalar CakePHP es utilizando Composer, una utilidad que se ejecuta en linea de
comandos y permite resolver las dependencias de librerias de una aplicación en PHP, descargarla y mantener
actualizadas esas librerías. Vamos a instalar Composer en primer lugar.

7.2.1. Composer en Linux y macOS.


Para instalar Composer en sistemas Unix/Linux y macOS, abra un terminal y ejecute las siguientes líneas:

$ curl -sS https://getcomposer.org/installer | php


$ sudo mv composer.phar /usr/local/bin/composer

Si no tiene instalado el comando curl o no funciona por cualquier motivo, como alternativa ejecute las
siguientes sentencias en línea de comandos:

$ php -r "readfile('https://getcomposer.org/installer');" | php

1
Curso PHP Medio Escuela de Administración Pública

$ sudo mv composer.phar /usr/local/bin/composer

Para comprobar que Composer está instalado correctamente ejecute la utilidad preguntando por la versión actual:

$ composer --version

Debe aparecer algo como esto:

Composer version 1.4.1 2017-03-10 19:29:45

Para terminar los preparativos para instalar CakePHP en nuestro sistema vamos a incluir dos extensiones de PHP
que este framework necesita para funcionar. Estas extensiones son intl y mbstring.
En el caso de linux es necesario instalar los dos paquetes desde el terminal de la siquiente manera:

$ sudo apt-get install php7.0-mbstring


$ sudo apt-get install php7.0-intl

Sin embargo, en macOS solo es necesario instalar la extensión intl. Para ello usamos la utilidad Homebrew:

$ brew install homebrew/php/php70-intl

7.2.2 Composer en Microsoft Windows.


Para instalar Composer en Microsoft Windows descargue el fichero de la siguiente URL:
https://getcomposer.org/Composer-Setup.exe y proceda con su instalación. El asistente de instalación le pedirá la
ruta donde está instalado el intérprete de PHP (php.exe). Busque en el directorio en el que realizó la instalación de
php. La forma de comprobar que tiene instalado correctamente Composer es similar a la utilizada en las plataformas
Linux y macOS. Haga clic en el icono de Windows y en el campo de búsqueda escriba el comando cmd y pulse
Enter. Se abrira una ventana de terminal. En ella escriba el comando siguiente:

C:\<ruta>\>composer –version

Debe aparecer algo similar a lo visto en Linux/macOS:

Composer version 1.4.1 2017-03-10 19:49:15

Compruebe también en php que las extensiones intl y mbstring estén ya incluidas y activas. Para ello es
necesario modificar el archivo de configuración de PHP (php.ini). Acceda a ese archivo y localize la linea siguiente:

;extension=php_intl.dll

Elimine el punto y coma inicial, guarde el fichero y reinicie Apache.

7.2.3. Instalación de Cake PHP.


Para instalar CakePHP con los permisos correctos y con el archivo de configuración creado (app.php) va a
utilizar la utilidad Composer recien instalada. Para ello, abra un terminal, acceda primero a la carpeta donde se
almacenan los proyectos de Apache (/var/www/ en Linux, /User/nombre_usuario/Sites en macOS y en la ruta
correspondiente en Windows). Una vez allí, ejecute el siguiente comando:

$ composer create-project --prefer-dist cakephp/app catalogo

Nota: El último parámetro indica el nombre de la carpeta que se creará para albergar el
proyecto de CakePHP.
Aparecen por pantalla varios mensajes informativos durante la instalación y se pausa para preguntar si desea
establecer los permisos de algunas carpetas. Pulse Enter para que realice esta acción ya que, en caso contrario,
podríamos tener problemas de permisos y, por tanto, CakePHP no funcionaría correctamente.

2
Curso PHP Medio Escuela de Administración Pública

Para conocer algunos de los directorios que contiene el framework, acceda a la carpeta catalogo. En este lugar
puede ver el directorio vendor, que contiene todas las librerías utilizadas, incluso el núcleo de CakePHP. También
puede ver la carpeta del proyecto el directorio src, que contiene el código del proyecto que quiere construir. Esta
carpeta contiene otras subcarpetas cuyos nombres le pueden resultar familiares. Como ya hemos indicado CakePHP
sigue el esquema MVC por lo que consta de controladores, modelos y vistas. Para contener las clases que
representan a estos tres elementos existen los directorios Controller, Model y Template (View).
En la carpeta Controller no existe aún ningún controlador propio de la aplicación de ejemplo, aunque sí existe el
controlador principal AppController en el fichero AppController.php, que es del que heredarán los nuevos
controladores, así como el controlador para los errores (ErrorController.php) y el de la página de inicio
(PageController.php). A medida que vaya creando controladores se irán almacenando aquí.
Los modelos contienen la lógica de negocio, para ello en el directorio Model se incluyen tres carpetas: Behaviour,
Entity y Table. Nos centraremos en Entity y Table para ver cómo accedemos a la información almacenada en base
de datos.
Finalmente, las vistas contenidas en el directorio Templates, constan de una carpeta por cada módulo que cree,
con un archivo por cada vista propia del controlador. Cada vista se asocia automáticamente con una acción de ese
controlador.
Otro directorio a tener en cuenta es config. Aquí se encuentran diversos ficheros relacionados con la configuración
de la aplicación que permiten establecer parámetros como conexiones a bases de datos, servicios de correo o activar
la depuración para obtener información cuando se produzcan errores o advertencias.
Hay dos consideraciones muy importantes que hacer antes de terminar la instalación. La primera se refiere a
cómo utiliza CakePHP las rutas indicadas en las URL de acceso. Como hemos visto anteriormente, el enrutado de
las URL se hace mediante reescritura con el módulo rewrite de Apache. CakePHP aprovecha esta utilidad
mediante un componente propio. Así, si queremos llamar a un método de una clase controladora pasando
parámetros se utiliza el esquema http://servidor/controlador/accion/parametro1/parametro2/.... Esta característica
debe ser implementada por el servidor web en el que despliegue CakePHP. Como también se indicó anteriormente,
en el ejemplo usará Apache que ya incluye esta característica a través de un módulo llamado mod_rewrite que
está activo para los tres sistemas. Puede que sea solo necesario activarlo en el caso de macOS.
La instalación está completa. Veamos cómo se ve nuestro proyecto, aún vacío, desde el navegador. Acceda a
http://localhost/catalogo. Debe tener un aspecto similar al de la figura 7.1.

Figura 7.1. Aspecto de un proyecto CakePHP vacío.

3
Curso PHP Medio Escuela de Administración Pública

Ésta es la página por defecto que CakePHP crea para mostrar información al acceder a la aplicación. Su utilidad
radica en que hace una serie de indicaciones importantes para hacer funcionar el proyecto CakePHP.
En esta página se alerta sobre configuraciones correctas, en color verde y algún error en color rojo. Una vez que
consiga tener casi todos los mensajes en verde, al menos el de error, puede continuar con el desarrollo del proyecto.
La información se agrupa en cuatro secciones:
• Environment, muestra el resultado de comprobar las versiones tanto de PHP como de las extensiones que
son necesarias para hacer funcionar CakePHP. En nuestro caso disponemos de las versiones correctas.
• Filesystem, en ella se hace referencia a ciertos directorios del arbol de ficheros del proyecto que tienen que
tener permisos de escritura (tmp y logs), así como la modalidad de caché que se ha configurado.
• DebugKit, simplemente indica si se ha cargado este plugin, que proporciona una barra de herramientas
para facilitar la depuración del proyecto.
• Database, la única por ahora en rojo, con mensaje de error, indica si CakePHP es capaz de conectarse a la
base de datos configurada por defecto. En este caso, como es obvio, como no hemos indicado aun los
parámetros de acceso a la base de datos, aparece un mensaje de error.
Para subsanar el error vamos a modificar el archivo de configuración de la aplicación situado en el directorio
config con el nombre app.php. Ese fichero viene con una configuración de conexión a base de datos de ejemplo,
pero necesita una conexión real a una base de datos.
Para este ejemplo cree un esquema de base de datos en MySQL y conéctelo al proyecto. Abra MySQL
WorkBench y cree un nuevo esquema de base de datos con el nombre catalogo. Acceda al fichero config/app.php.
Como puede observar, este fichero contiene muchos más parámetros de configuración. Para acceder a los
parámetros concretos de las conexiones a bases de datos busque un apartado con el título Datasources. Su
estructura es la de un array, por lo que puede indicar varias conexiones a bases de datos. De hecho, existe a modo de
ejemplo una conexión con la clave default y otra con la clave test. Si no se indica lo contrario, CakePHP utiliza
la conexión default para su conexión y la de test para realizar las pruebas unitarias. Acceda a la configuración
de la conexión default y modifique sus valores para que sean los del listado (se han eliminado los comentarios a
efectos de claridad).

'Datasources' => [
'default' => [
'className' => 'Cake\Database\Connection',
'driver' => 'Cake\Database\Driver\Mysql',
'persistent' => false,
'host' => 'localhost',
'username' => 'root',
'password' => 'root',
'database' => 'catalogo',
'encoding' => 'utf8',
'timezone' => 'UTC',
'flags' => [],
'cacheMetadata' => true,
'log' => false,
'quoteIdentifiers' => false,
'url' => env('DATABASE_URL', null),
],

Si actualiza la página verá un mensaje en verde informando que la aplicación puede acceder a una base de datos.
Ya está todo listo para empezar con el código del ejemplo.

7.3. Convenciones
Utilice MySQL WorkBench para crear las tablas que necesita. Construya una tabla con el nombre productos y
los campos id, nombre, precio y tipo_id y otra tabla tipos con los campos id y nombre. No se preocupe,
en breve trataremos el por qué de esos nombres. El aspecto de las tablas tiene que ser similar al de las figuras 7.2 y
7.3.

4
Curso PHP Medio Escuela de Administración Pública

Figura 7.2. Campos de la tabla Productos.

Figura 7.3. Campos de la tabla Tipos.

Al comienzo del capítulo, señalamos que CakePHP utiliza convención sobre configuración. Al crear estas tablas
ya está siguiendo algunas convenciones.
La primera se refiere al nombre de controladores, modelos y tablas. Por defecto, CakePHP se basa en el nombre
de un controlador para obtener el nombre del modelo y el nombre de la tabla de la base de datos, así como el
nombre de la carpeta de las vistas, cumpliendo así la ruta completa para construir el patrón MVC de forma
automática.
Para ello establece que:
• el nombre de los controladores y la carpeta de vistas tiene que ser plural,
• el de los modelos singular en las entidades y plural en las tablas;
• y finalmente, el de las tablas de base de datos en plural.
Es decir, esto se resumen en que todo va en plural excepto el nombre de las entidades de los modelos.
Por ejemplo, si accede a http://localhost/catalogo/productos/index, la aplicación debería enrutar la petición para
que acceda al controlador ProductosController, a un método con el nombre index(), desde el que accederá
a la clase ProductosTable, y éste a su vez a la tabla productos. Finalmente, el resultado se muestra en la vista
index.ctp del directorio Template/Productos/.
Advertencia: Al ser un proceso automático la resolución de plurales y singulares basado
en el nombre del controlador, Cakephp ha de basarse en algún idioma. Por defecto,
CakePHP resuelve los plurales y singulares en inglés, no en castellano, por lo que debe
tener especial cuidado al nombrar controladores y tablas con plurales irregulares. Por
ejemplo, si utiliza un controlador Poblaciones, CakePHP buscará un modelo
Poblaciones y una tabla poblaciones. En adelante, utilizaremos la configuración por
defecto de CakePHP utilizando plurales regulares que no creen confusión. Aun así, tenga
cuidado porque los plurales latinos como Categorias son resueltos por CakePHP con su
singular en latín, Categorium. Si va a utilizar la configuración por defecto es
recomendable que utilice nombres en inglés ya que es muy probable que necesite tener
algún modelo con plural irregular o configurar CakePHP para que controle los plurales
regulares en castellano. CakePHP permite configurar la resolución de plurales y
singulares en otros idiomas.

5
Curso PHP Medio Escuela de Administración Pública

Todas las tablas en el esquema de base de datos, por defecto, se han de nombrar en plural, es decir, para que el
modelo de CakePHP pueda acceder a una tabla concreta de un controlador debe cumplir esta norma. Para nuestro
ejemplo utilizamos las tablas productos y tipos, por lo que sus controladores se llamarán
ProductosController y TiposController respectivamente. Además, sus modelos se componen de las
parejas de clases Producto, ProductosTable y Tipo, TiposTable. Las carpetas de vistas, View/Productos y
View/Tipos. Como puede observar, al ser plurales regulares no generan confusión.
Nota: Una diferencia a destacar con versiones anteriores de CakePHP es la forma en la
que se utilizan los modelos. Anteriormente solo existía un único fichero con la
responsabilidad de lidiar con la lógica de negocio almacenada en una base de datos. En la
versión actual, esa responsabilidad se distribuye en dos clases Entity y Table. Los
objetos de tabla (Table) o repositorios proporcionan acceso a colecciones de datos.
Permiten guardar nuevos registros, modificar o eliminar los existentes, definir relaciones
entre modelos y realizar operaciones masivas sobre esos datos. Sin embargo, las
entidades (Entity) representan registros individuales de datos y permiten definir el
comportamiento y la funcionalidad a ese nivel. Todos los modelos constan de una clase
que hereda de Table y otra de Entity. En nuestro ejemplo, como ya hemos indicado
más arriba son Tipo y TiposTable para tipos; y Producto y ProductosTable
para productos. Puede ver también, que están dispuestos en sus carpetas
correspondientes en el directorio Model.
Otra de las convenciones más importantes es la que hace referencia a los índices primarios de las tablas, que han
de llamarse con el nombre id. Cuando CakePHP accede a una tabla a través de su modelo busca este campo como
clave primaria y la utiliza para establecer relaciones con otras tablas.
CakePHP permite establecer relaciones 'pertenece a', 'uno a uno', 'uno a muchos' y 'muchos a muchos' entre los
modelos. En el ejemplo hemos utilizado una relación 'uno a muchos' entre productos y tipos: un tipo puede tener
muchos productos; y una relación 'pertenece a': un producto pertenece a un tipo. Por convención, para crear esta
relación y que sea reconocible por CakePHP se ha de incluir la clave primaria de tipos en la tabla productos como
una clave ajena. Para no depender del motor de base de datos en la declaración de claves ajenas, CakePHP establece
que una clave es ajena cuando su nombre se compone del nombre en singular de otra tabla seguida del sufijo '_id'.
Así, en nuestro ejemplo, la clave ajena de categoría en productos se llama tipo_id.
Existen muchas más convenciones en cuanto a modelos y relaciones, pero sobrepasan el objetivo de este curso.
Para más información acceda a la página oficial de CakePHP (https://cakephp.org/).

7.4. Página de inicio y menú


En la página inicial de la aplicación existe información que ya no necesitamos. Para eliminar ese contenido y
añadir el que necesita, abra el archivo catalogo/src/Template/Pages/home.ctp, elimine su contenido e incluya el
siguiente código HTML:

<h1>Bienvenidos al catálogo de Productos</h1>

Es en ese archivo donde puede incluir todo el contenido que desee que aparezca cuando cualquier usuario acceda
inicialmente a su aplicación, es la página de inicio o home.
Tanto el título de la página en la cabecera como el título de la ventana tienen el texto que CakePHP configura por
defecto. Para personalizar estos contenidos tiene que acceder a un archivo concreto que contiene todo el código
HTML y PHP que se ejecuta cada vez que se muestra una vista. Es el contenedor encargado de cargar las etiquetas
principales de HTML así como las hojas de estilo y los archivos de javascript. En ese contenedor o layout se carga
cada vista cada vez que se hace una llamada a la acción de un controlador. Existe un layout por defecto que es el que
siempre es utilizado como contenedor si no se especifica otro. Acceda a él para modificar algunos elementos
importantes, como los títulos e incluir un pequeño menú para acceder a la gestión de tipos y productos. Al
mostrarse con cada vista, si incluye un menú en este contenedor, sera accesible desde todas las vistas.
Ese archivo se encuentra en catalogo/src/Template/Layout/default.ctp. Si observa su contenido podrá detectar
variables para establecer un título para la aplicación (<?=$this->fetch('title')?>), métodos para cargar la hoja de

6
Curso PHP Medio Escuela de Administración Pública

estilos por defecto de CakePHP ( <?=$this->Html->css('cake.css')?>) y para cargar scripts de javascript generados
desde CakePHP. En el siguiente listado se encuentra el contenido de ese layout para el ejemplo:
<?php $appDescription = 'Catálogo';?>
<!DOCTYPE html>
<html>
<head>
<?=$this->Html->charset()?>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>
<?=$appDescription?>:
<?=$this->fetch('title')?>
</title>
<?=$this->Html->meta('icon')?>

<?=$this->Html->css('base.css')?>
<?=$this->Html->css('cake.css')?>

<?=$this->fetch('meta')?>
<?=$this->fetch('css')?>
<?=$this->fetch('script')?>
</head>
<body>
<nav class="top-bar expanded" data-topbar role="navigation">
<ul class="title-area large-3 medium-4 columns">
<li class="name">
<h1><a href=""><?=$this->Html->link($appDescription, '/')?></a></h1>
</li>
</ul>
<div class="top-bar-section">
<ul class="left">
<li><?=$this->Html->link('Tipos', ['controller'=>'Tipos', 'action'=>'index'])?></li>
<li><?=$this->Html->link('Productos', ['controller'=>'Productos', 'action'=>'index'])?></li>
</ul>
</div>
</nav>
<?=$this->Flash->render()?>
<div class="container clearfix">
<?=$this->fetch('content')?>
</div>
<footer></footer>
</body>
</html>

Cada vez que acceda a una vista, si no indica otro layout, CakePHP cargará su contenido utilizando la función
fetch(), en la etiqueta <div> con el identificador content. Si además quiere enviar mensajes al usuario que
informen sobre el estado de cada acción, el método render() del ayudante HTML Flash mostrará todos los
mensajes que establezca en el controlador al que llame.
Puede ver el resultado del listado accediendo a su proyecto en http://localhost/catalogo. Tiene que ser similar al
que se muestra en la figura 7.4.

Figura 7.4. Página de inicio y menú.

7
Curso PHP Medio Escuela de Administración Pública

Al hacer clic en el título de la aplicación le llevará siempre a la página de inicio. Si pincha en los enlaces del menú
se mostrarán varios errores debido a que aun no ha generado los módulos a los que intenta acceder.

7.5. Crear módulo básico


En primer lugar va a crear un módulo básico completo, entendiendo por módulo como el conjunto de clases
necesarias para crear, actualizar, eliminar y acceder al listado de registros de una tabla de la base de datos, lo que se
denomina con el acrónimo CRUD (create, read, update y delete). Estas clases componen la estructura MVC
necesaria para la gestión de la información de una tabla y sus relaciones.
Para el ejemplo comenzaremos con el módulo de tipos. Cree en primer lugar el modelo con los ficheros de
entidad y de tabla. Cree el archivo catalogo/src/Model/Entity/Tipo.php y añada el contenido del listado siguiente:

<?php
namespace App\Model\Entity;
use Cake\ORM\Entity;

class Tipo extends Entity


{
protected $_accessible = [
'*' => true,
'id' => false
];
}

Con estas pocas lineas de código ha creado la entidad para la tabla tipos. La propiedad $_accesible permite
indicar un mapa de los campos de la entidad, que se corresponden con los campos de la tabla de la base de datos,
que son accesibles para ser modificados de forma masiva. En este caso, se permite acceder a todos los campos de la
entidad Tipo excepto al campo identificador id. Hay que tener en cuenta que, aunque esta forma de asignación es
simple y facilita esa tarea, puede tener consecuencias importantes a nivel de seguridad. Por simplificar nuestro
ejemplo lo mantendremos así, pero tenga en cuenta que si añade campos a la tabla también estarán disponibles de
forma automática.
Veremos ahora el otro componente del modelo: TiposTable, localizado en
catalogo/src/Model/Table/TiposTable.php. Cree ese archivo e incluya el siguiente código:
<?php
namespace App\Model\Table;

use Cake\ORM\Query;
use Cake\ORM\RulesChecker;
use Cake\ORM\Table;
use Cake\Validation\Validator;

class TiposTable extends Table


{

public function initialize(array $config)


{
parent::initialize($config);

$this->setTable('tipos');
$this->setDisplayField('nombre');
$this->setPrimaryKey('id');

$this->hasMany('Productos', [
'foreignKey' => 'tipo_id'
]);
}

public function validationDefault(Validator $validator)


{
$validator
->integer('id')
->allowEmpty('id', 'create');

8
Curso PHP Medio Escuela de Administración Pública

$validator
->requirePresence('nombre', 'create')
->notEmpty('nombre');

return $validator;
}
}
En primer lugar, se define el método que inicializa la clase, en la que se establece el nombre de la tabla de la base
de datos con la que está relacionada. A continuación define la variable $displayField para indicar qué campo
del modelo se utilizará como campo por defecto a mostrar cuando se utilice en los modelos relacionados, en nuestro
caso, el nombre del tipo. Y, finalmente, indica cuál es el campo que actúa como clave primaria.
En ese mismo método se define la relación con la tabla productos, estableciendo que ese modelo es gestionado
por la clase Productos (realmente ProductosTable) y que la clave ajena utilizada para crear la relación es el
campo tipo_id. Aún no ha generado el modelo de productos por lo que puede suponer que generará algún error.
En este caso no será así porque al usar sólo el módulo de tipos no va a acceder todavía a datos relacionados.
Una vez descritas la entidad y la tabla, debe crear el controlador, que será la clase responsable de recibir las
peticiones relacionadas con la información de tipos. Las acciones a definir tanto para este controlador como para el
de productos serán:
• index(). Es la acción que CakePHP busca por defecto cuando no se le indica ninguna. En ella obtiene un
listado de registros que serán mostrados en su vista en forma de tabla de datos paginada, con la opción de
ordenar por los campos mostrados.
• view(). Con esta acción simplemente se muestran la información del registro, sin formulario para realizar
cambios.
• add(). Esta acción realmente se compone de dos, una para mostrar el formulario para obtener los datos de
un nuevo registro y otra para añadir esos datos como un nuevo registro a la tabla de datos. El hacer una u
otra opción depende de si se le envian datos a la acción.
• edit(). Es la que lanza cuando quiera actualizar la información de uno de los registros. También se
compone de dos acciones: una para mostrar el formulario con los datos del registro a modificar y otra para
actualizar los datos modificados en la tabla de datos.
• delete(). Finalmente, la acción que completa el CRUD, es decir, la responsable de eliminar el registro
que se le indique.

7.5.1. Listado de registros


Construya el controlador generando un fichero con el nombre TiposController.php en la carpeta
catalogo/src/Controller/. Esa será la clase responsable de recibir las peticiones relacionadas con la información de
tipos. El contenido inicial de este controlador es el siguiente:
<?php
namespace App\Controller;
use App\Controller\AppController;

class TiposController extends AppController


{
public function index()
{
$tipos = $this->Tipos->find('all');
$this->set(compact('tipos'));
}
}
Inicialmente este método realiza una consulta al modelo Tipo para obtener todos sus registros y lo asigna con la
función set() a la variable $tipos que será la que usaremos en la vista de esa acción. Esa vista mostrará todos
esos registros en una tabla de datos recorriendo el array $tipos suministrado por el controlador. Para ello genere
un nuevo archivo catalogo/src/Template/Tipos/index.ctp con el contenido del siguiente listado:
<nav class="large-3 medium-4 columns" id="actions-sidebar">
<ul class="side-nav">
<li class="heading">Acciones</li>
<li><?=$this->Html->link('Nuevo Tipo', ['action'=>'add'])?></li>

9
Curso PHP Medio Escuela de Administración Pública

<li><?=$this->Html->link('Productos', ['controller'=>'Productos', 'action'=>'index'])?></li>


<li><?=$this->Html->link('Nuevo Producto', ['controller'=>'Productos', 'action'=>'add'])?></li>
</ul>
</nav>
<div class="tipos index large-9 medium-8 columns content">
<h3>Tipos</h3>
<table cellpadding="0" cellspacing="0">
<thead>
<tr>
<th scope="col">Id</th>
<th scope="col">Nombre</th>
<th scope="col" class="actions">Acciones</th>
</tr>
</thead>
<tbody>
<?php foreach($tipos as $tipo): ?>
<tr>
<td><?=$this->Number->format($tipo->id)?></td>
<td><?=$tipo->nombre?></td>
<td class="actions">
<?=$this->Html->link('Ver', ['action'=>'view', $tipo->id]) ?>
<?=$this->Html->link('Editar', ['action'=>'edit', $tipo->id])?>
<?=$this->Form->postLink('Eliminar', ['action'=>'delete', $tipo->id],
['confirm'=>'¿Realmente quiere eliminar?'])?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>

Acceda desde el navegador a http://localhost/catalogo y haga clic en el menú en la opción Tipos o acceda a
http://localhost/catalogo/tipos. Verá un listado sin ningún registro, con los nombres de los campos y con un menú
para acciones donde aparece un botón para crear un nuevo tipo (vea la figura 7.5).

Figura 7.5. Listado vacío de tipos.

7.5.2. Nuevo registro


Es el momento de añadir nuevos tipos. Si pincha en el botón con el título Nuevo Tipo, se mostrarán varios
errores debido a que aun no ha implementado la acción para añadir registros. Acceda de nuevo al controlador de
tipos y añada el código descrito en el siguiente listado.

public function add()


{
$tipo = $this->Tipos->newEntity();
if($this->request->is('post')){
$tipo = $this->Tipos->patchEntity($tipo, $this->request->getData());
if($this->Tipos->save($tipo)){
$this->Flash->success('Tipo guardado.');
return $this->redirect(['action'=>'index']);
}
$this->Flash->error('Error al guardar el tipo');
}
$this->set(compact('tipo'));
}

10
Curso PHP Medio Escuela de Administración Pública

Con este método se realizan dos acciones dependiendo si se le envían datos o no por formulario. Si no se le
envían datos, esta acción se limita a mostrar su vista, que es el formulario con los campos para crear un nuevo tipo.
Si se le envían datos mediante el método POST, el controlador llama al modelo para crear una nueva entidad y
guardarla con los datos pasados. Para ello, mediante el método patchEntity() primero se validan los datos
pasados y posteriormente se asignan a las propiedades de la entidad. A continuación, el objeto resultante se
almacena en la tabla de base de datos con el método save(). Si esa acción tiene éxito lo comunica al usuario con
un mensaje mediante el método success() del componente Flash y redirige la petición al listado de tipos. Si,
por el contrario, no se ha podido almacenar el registro, simplemente lo comunica al usuario con un mensaje de
error, pero éste permanece en el formulario para añadir tipos.
Complete esta funcionalidad con el contenido del listado a continuación, creando la vista con el formulario para
añadir un nuevo registro en catalogo/src/Template/Tipos/add.ctp.
<nav class="large-3 medium-4 columns" id="actions-sidebar">
<ul class="side-nav">
<li class="heading">Acciones</li>
<li><?=$this->Html->link('Tipos', ['action'=>'index'])?></li>
<li><?=$this->Html->link('Productos', ['controller'=>'Productos', 'action'=>'index'])?></li>
<li><?=$this->Html->link('Nuevo Producto', ['controller'=>'Productos', 'action'=>'add'])?></li>
</ul>
</nav>
<div class="tipos form large-9 medium-8 columns content">
<?=$this->Form->create($tipo)?>
<fieldset>
<legend>Nuevo Tipo</legend>
<?=$this->Form->control('nombre')?>
</fieldset>
<?=$this->Form->button('Crear')?>
<?=$this->Form->end()?>
</div>

Puede ver el resultado en la figura 7.6.

Figura 7.6. Formulario para crear un nuevo tipo.

Cree varios registros de tipos y observe cómo va aumentando el listado de tipos (figura 7.7). Por cada registro, en la
vista del listado, se muestran tres nuevos botones para ver el tipo, editarlo o eliminarlo. En esos botones se utiliza el
ayudante Html y Form de CakePHP para crear enlaces como elementos de acción. En el enlace para eliminar, se
incluye además la opción de que cuando se haga clic en él se muestre un diálogo con un mensaje para confirmar la
eliminación.

11
Curso PHP Medio Escuela de Administración Pública

Figura 12.7. Nuevos tipos.

El mensaje de información que creó con el ayudante Flash en el controlador se muestra en una banda en color
azul debajo de la barra de menú.

7.5.3. Paginar y ordenar


Observe que a medida que va creando registros va llegando al límite de la página. Para evitar que aparezcan tantos
registros que sobrecarguen el acceso y sea necesario desplazarnos por la página para verlos todos, éstos se suelen
paginar. Para ello se muestra un conjunto de registros por página y enlaces para acceder al resto de páginas, de forma
que siempre se vean como máximo los registros de una página.
CakePHP permite el uso de páginas de una forma muy sencilla. Para ello utiliza un componente llamado
Paginate. Un componente es una utilidad común que puede utilizarse en multitud de controladores. De igual
forma, para mostrar esa paginación en las vistas, CakePHP utiliza un ayudante. El listado a continuación muestra el
único cambio necesario en el controlador para poder utilizar la paginación.

public $paginate = ['maxLimit' => 10];

public function index()


{
$tipos = $this->paginate($this->Tipos);
$this->set(compact('tipos'));
}

Con ese simple cambio ya tenemos disponibles la paginación desde el controlador, con páginas de diez registros.
Por defecto, CakePHP establece el límite en 100, por lo que es necesario redefinir esa variable a nivel de controlador
para acotar las páginas. Para paginar establece la variable $tipos con el método paginate(), es decir, realiza
también la consulta de los datos al igual que hicimos con el método find() pero paginando los resultados. El
contenido de la vista index.ctp queda de la siguiente forma:
<nav class="large-3 medium-4 columns" id="actions-sidebar">
<ul class="side-nav">
<li class="heading">Actions</li>
<li><?=$this->Html->link('Nuevo Tipo', ['action'=>'add'])?></li>
<li><?=$this->Html->link('Productos', ['controller'=>'Productos', 'action'=>'index'])?></li>
<li><?=$this->Html->link('Nuevo Producto', ['controller'=>'Productos', 'action'=>'add'])?></li>
</ul>
</nav>
<div class="tipos index large-9 medium-8 columns content">
<h3>Tipos</h3>
<table cellpadding="0" cellspacing="0">
<thead>
<tr>
<th scope="col"><?=$this->Paginator->sort('id')?></th>

12
Curso PHP Medio Escuela de Administración Pública

<th scope="col"><?=$this->Paginator->sort('nombre')?></th>
<th scope="col" class="actions">Acciones</th>
</tr>
</thead>
<tbody>
<?php foreach($tipos as $tipo):?>
<tr>
<td><?=$this->Number->format($tipo->id)?></td>
<td><?= h($tipo->nombre)?></td>
<td class="actions">
<?=$this->Html->link('Ver', ['action'=>'view', $tipo->id])?>
<?=$this->Html->link('Editar', ['action'=>'edit', $tipo->id])?>
<?=$this->Form->postLink('Eliminar', ['action'=>'delete', $tipo->id],
['confirm'=>'¿Realmente quiere eliminar?'])?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<div class="paginator">
<ul class="pagination">
<?=$this->Paginator->first('<< primero')?>
<?=$this->Paginator->prev('< anterior')?>
<?=$this->Paginator->numbers()?>
<?=$this->Paginator->next('siguiente >')?>
<?=$this->Paginator->last('último >>')?>
</ul>
<p><?=$this->Paginator->counter(['format'=>'Página {{page}} de {{pages}},
mostrando {{current}} registro(s) de {{count}}'])?></p>
</div>
</div>

En la figura 7.8 puede ver el resultado.

Figura 7.8. Registros paginados.

Al utilizar el componente y el ayudante Paginator no sólo aparecen los registros de datos paginados sino que
también se proporcionan a los usuarios botones para acceder a las diferentes páginas y para navegar a la página
anterior y a la siguiente. Además, si observa el código donde se definen los títulos de las columnas de la tabla de
datos, puede percibir que se han cambiado para utilizar la función sort() del ayudante Paginator. Con esta
funcionalidad, al hacer clic en los títulos de las columnas, aparecerán los registros ordenados por la columna que
seleccione, tanto de forma ascendente como ascendente al hacer clic consecutivamente.

7.5.4. Editar y actualizar registros


El usuario ya puede ver un listado paginado y ordenado de los registros de tipos y añadir nuevos tipos pero aun
no puede editar un registro para actualizar su información y eliminar aquellos que no necesite. El caso de la edición
es muy similar a añadir un nuevo registro con la diferencia de que, al editar, es necesario suministrar el identificador
del registro a actualizar para mostrar su información y para modificar ése y no otro registro.

13
Curso PHP Medio Escuela de Administración Pública

Es necesario que incluya una nueva acción en el controlador de tipos. Para ello abra el archivo
catalogo/src/Controller/TiposController.php y añada como nuevo método, a continuación de add(), el
implementado en el listado a continuación.

public function edit($id = null)


{
$tipo = $this->Tipos->get($id, [
'contain' => []
]);
if($this->request->is(['patch', 'post', 'put'])){
$tipo = $this->Tipos->patchEntity($tipo, $this->request->getData());
if($this->Tipos->save($tipo)){
$this->Flash->success('Tipo actualizado.');
return $this->redirect(['action' => 'index']);
}
$this->Flash->error('Error al actualizar el tipo');
}
$this->set(compact('tipo'));
}

Este método es similar al de añadir, pero añade más funcionalidad. En primer lugar recibe el identificador del
registro a editar como parámetro de entrada. En segundo lugar, obtiene el registro de datos del tipo indicado con ese
identificador. Si ese identificador no existe o no se le pasa ningún identificador, CakePHP lanza una excepción
indicando que no se ha encontrado ese registro mostrando una página de error. Si desea personalizar ese mensaje
debería controlar la excepción mediante un bloque try-catch. Si existe ese registro, continua y, como sucede al
añadir, evalúa no solo si se le están enviando datos en formato POST, sino también en formato PATCH o PUT. Si
no es así, quiere decir que la aplicación debe mostrar el formulario de edición con los datos del registro obtenido al
principio y pasados con set() a la vista en la variable $tipo.
Finalmente, como sucede al añadir, si llegan datos del formulario en formato POST, PATCH o PUT, se
almacenan con save(), se informa al usuario del éxito y se redirecciona al listado de registros. Si la operación
fracasa se advierte del error y se vuelve a mostrar el formulario de edición.
Para continuar con la edición es necesario crear su vista. Cree el fichero catalogo/src/Template/Tipos/edit.ctp e
incluya el código siguiente:
<nav class="large-3 medium-4 columns" id="actions-sidebar">
<ul class="side-nav">
<li class="heading">Acciones</li>
<li><?=$this->Form->postLink('Eliminar', ['action'=>'delete', $tipo->id],
['confirm'=>'¿Realmente quiere eliminar?'])?></li>
<li><?=$this->Html->link('Tipos', ['action'=>'index'])?></li>
<li><?=$this->Html->link('Productos', ['controller'=>'Productos', 'action'=>'index'])?></li>
<li><?=$this->Html->link('Nuevo Producto', ['controller'=>'Productos', 'action'=>'add'])?></li>
</ul>
</nav>
<div class="tipos form large-9 medium-8 columns content">
<?=$this->Form->create($tipo)?>
<fieldset>
<legend>Editar Tipo</legend>
<?=$this->Form->control('nombre')?>
</fieldset>
<?=$this->Form->button('Actualizar')?>
<?=$this->Form->end()?>
</div>

Este formulario es prácticamente igual al de añadir tipos. Se diferencia en el botón de eliminar en el que se
incluye el identificador del registro. Por lo demás es idéntico. La figura 7.9 que contiene la imagen de la edición del
tipo 'Alimentación' es igual que la del formulario al añadir con la diferencia de que el contenido del registro se
incluye en los campos.

14
Curso PHP Medio Escuela de Administración Pública

Figura 7.9. Formulario de edición.

Modifique sus registros y observará como al regresar al listado de tipos, éstos aparecerán con los cambios que haya
realizado.

7.5.5. Ver un registro


Suele suceder que el usuario desee ver la información de un registro sin la necesidad de usar un formulario para
alterar sus datos o simplemente, por seguridad, queremos que se pueda consultar la información pero no alterarla.
Para ello hemos creado una acción y una vista nueva que permiten presentar la información de un registro de
forma sencilla. Realmente funciona de forma muy similar a la edición de un registro pero sin permitir guardar sus
datos en el controlador y usando solo texto para mostrar la información.
Vamos a crear un nuevo método en el controlador de tipos con el código siguiente:

public function view($id=null)


{
$tipo = $this->Tipos->get($id, [
'contain' => []
]);
$this->set('tipo', $tipo);
}

Como puede observar, es una versión simplificada del método edit(). Obtiene la información del registro
mediante su id y lo pasa a la variable que usamos en la vista.
La vista es aun más simple. Incluya el siguiente código en el fichero catalogo/src/Template/Tipos/view.ctp:
<nav class="large-3 medium-4 columns" id="actions-sidebar">
<ul class="side-nav">
<li class="heading">Acciones</li>
<li><?=$this->Html->link('Editar', ['action'=>'edit', $tipo->id])?></li>
<li><?=$this->Form->postLink('Eliminar', ['action'=>'delete', $tipo->id],
['confirm'=>'¿Realmente desea eliminar?'])?></li>
<li><?=$this->Html->link('Tipos', ['action'=>'index'])?></li>
<li><?=$this->Html->link('Nuevo Tipo', ['action'=>'add'])?></li>
<li><?=$this->Html->link('Productos', ['controller'=>'Productos', 'action'=>'index'])?></li>
<li><?=$this->Html->link('Nuevo Producto', ['controller'=>'Productos', 'action'=>'add'])?></li>
</ul>
</nav>
<div class="tipos view large-9 medium-8 columns content">
<h3><?=h($tipo->nombre)?></h3>
<table class="vertical-table">
<tr>
<th scope="row">Id</th>
<td><?=$this->Number->format($tipo->id)?></td>
</tr>
<tr>
<th scope="row">Nombre</th>
<td><?=h($tipo->nombre)?></td>
</tr>
</table>
</div>

15
Curso PHP Medio Escuela de Administración Pública

7.5.6. Eliminar un registro


La última operación por completar es la de eliminar registros. Esta acción ya la ha habilitado en varios lugares
mediante el menú de la izquierda. Concretamente en las vistas edit.ctp, y view.ctp. Desde la vista del listado de
registros en el archivo catalogo/src/Template/Tipos/index.ctp, en cada fila de la tabla de datos ha incluido un
enlace para llamar a la acción del controlador que debe eliminar registros: delete().
Para crear este enlace ha utilizado el ayudante de CakePHP para los formularios $this->Form, accediendo a su
utilidad postLink(), cuyos parámetros le permiten especificar el título del enlace, la acción a llamar con sus
parámetros y, en este caso, una solicitud de confirmación a través de una ventana de diálogo.

<?=$this->Form->postLink('Eliminar', ['action'=>'delete', $tipo->id],


['confirm'=>'¿Realmente desea eliminar?'])?>

Figura 7.10. Diálogo de confirmación.

Si hace clic en Aceptar, será necesario que el controlador disponga de la acción delete(), de lo contrario la
aplicación generará un error.
Acceda al controlador de tipos en catalogo/src/Controller/TiposController.php y añada el método del siguiente
listado al final de la clase.

public function delete($id = null)


{
$this->request->allowMethod(['post', 'delete']);
$tipo = $this->Tipos->get($id);
if($this->Tipos->delete($tipo)){
$this->Flash->success('Tipo eliminado.');
} else {
$this->Flash->error('Error al eliminar el tipo.');
}
return $this->redirect(['action' => 'index']);
}

Como sucede al editar, al eliminar es necesario indicar qué registro se desea eliminar. Para ello se le pasa el
identificador y se comprueba si existe. Si no existe se provoca una excepción y, en caso contrario, obtiene una
entidad Tipo desde el repositorio Tipos con el método get(). A continuación, solo es necesario invocar el
método delete() del mismo repositorio pasando el objeto que se desea eliminar.
Si acepta eliminar el tipo, puede ver el resultado en la figura 7.11.

16
Curso PHP Medio Escuela de Administración Pública

Figura 7.11. Registro eliminado.

7.6. Crear módulo relacionado


El módulo para gestionar la tabla de Productos ha de incluir la relación que existe con Tipos. Empiece
creando el controlador de Productos y le iremos señalando las diferencias con el módulo básico realizado en
cuanto a mostrar la relación con Tipos.
Cree el archivo catalogo/src/Controller/ProductosController.php y añada los mismos métodos utilizados al crear
el módulo básico de Tipos adaptado a Productos.
<?php
namespace App\Controller;
use App\Controller\AppController;

class ProductosController extends AppController


{
public $paginate = ['maxLimit' => 10];

public function index()


{
$this->paginate = [
'contain' => ['Tipos']
];
$productos = $this->paginate($this->Productos);
$this->set(compact('productos'));
}

public function view($id = null)


{
$producto = $this->Productos->get($id, [
'contain' => ['Tipos']
]);
$this->set('producto', $producto);
}

public function add()


{
$producto = $this->Productos->newEntity();
if($this->request->is('post')){
$producto = $this->Productos->patchEntity($producto, $this->request->getData());
if($this->Productos->save($producto)){
$this->Flash->success('Producto creado.');
return $this->redirect(['action' => 'index']);
}
$this->Flash->error('Error al crear el producto.');
}

17
Curso PHP Medio Escuela de Administración Pública

$tipos = $this->Productos->Tipos->find('list', ['limit' => 200]);


$this->set(compact('producto', 'tipos'));
}

public function edit($id = null)


{
$producto = $this->Productos->get($id, [
'contain' => []
]);
if($this->request->is(['patch', 'post', 'put'])){
$producto = $this->Productos->patchEntity($producto, $this->request->getData());
if($this->Productos->save($producto)){
$this->Flash->success('Producto actualizado.');
return $this->redirect(['action' => 'index']);
}
$this->Flash->error('Error al actualizar el producto');
}
$tipos = $this->Productos->Tipos->find('list', ['limit' => 200]);
$this->set(compact('producto', 'tipos'));
}

public function delete($id = null)


{
$this->request->allowMethod(['post', 'delete']);
$producto = $this->Productos->get($id);
if($this->Productos->delete($producto)){
$this->Flash->success('Producto eliminado.');
} else {
$this->Flash->error('Error al eliminar el producto.');
}
return $this->redirect(['action' => 'index']);
}
}

Las diferencias fundamentales con el controlador de tipos se refieren a cómo se relacionan los productos con los
tipos. En el método index() se incluye una opción dentro de la variable de paginación para que, al hacer la
consulta, obtenga el registro relacionado de tipos, mediante el índice contain del array paginate. De esa forma,
al mostrar el campo del tipo relacionado en el listado de productos no se muestra un índice numérico sino el
nombre real del tipo de producto. Sucede lo mismo en el método view().
Al añadir un nuevo producto es necesario mostrar una lista de tipos de producto para que el usuario indique a
qué tipo pertenece. Para ello en el método add() se incluye una variable $tipos que almacena los registros de la
consulta que se hace a Tipos y se pasa a la vista junto al objeto $producto. Se procede de igual forma en el
método edit(). Más adelante veremos las diferencias en las vistas.
Cree el archivo de entidad para el modelo en catalogo/src/Model/Entity/Producto.php con el contenido del
listado siguiente.
<?php
namespace App\Model\Entity;
use Cake\ORM\Entity;

class Producto extends Entity


{
protected $_accessible = [
'*' => true,
'id' => false
];
}

Complete el modelo con el archivo para el repositorio catalogo/src/Model/Table/ProductosTable.php:


<?php
namespace App\Model\Table;

use Cake\ORM\Query;
use Cake\ORM\RulesChecker;
use Cake\ORM\Table;
use Cake\Validation\Validator;

class ProductosTable extends Table

18
Curso PHP Medio Escuela de Administración Pública

public function initialize(array $config)


{
parent::initialize($config);

$this->setTable('productos');
$this->setDisplayField('nombre');
$this->setPrimaryKey('id');

$this->belongsTo('Tipos', [
'foreignKey' => 'tipo_id',
'joinType' => 'INNER'
]);
}

public function validationDefault(Validator $validator)


{
$validator
->integer('id')
->allowEmpty('id', 'create');
$validator
->requirePresence('nombre', 'create')
->notEmpty('nombre');
$validator
->decimal('precio')
->requirePresence('precio', 'create')
->notEmpty('precio');

return $validator;
}

public function buildRules(RulesChecker $rules)


{
$rules->add($rules->existsIn(['tipo_id'], 'Tipos'));
return $rules;
}
}

El contenido es parecido al del modelo de Tipo con la diferencia de que la relación que define es una 'pertenece
a' con la variable $this->belongsTo, indicando la clase del modelo con la que se relaciona y el campo que actúa
como clave ajena.
Veamos los cambios que son necesarios en el fichero catalogo/src/Template/Productos/index.ctp con respecto al
mismo fichero que generamos para tipos.
<nav class="large-3 medium-4 columns" id="actions-sidebar">
<ul class="side-nav">
<li class="heading">Acciones</li>
<li><?=$this->Html->link('Nuevo Producto', ['action'=>'add'])?></li>
<li><?=$this->Html->link('Tipos', ['controller'=>'Tipos', 'action'=>'index'])?></li>
<li><?=$this->Html->link('Nuevo Tipo', ['controller'=>'Tipos', 'action'=>'add'])?></li>
</ul>
</nav>
<div class="productos index large-9 medium-8 columns content">
<h3>Productos</h3>
<table cellpadding="0" cellspacing="0">
<thead>
<tr>
<th scope="col"><?=$this->Paginator->sort('id')?></th>
<th scope="col"><?=$this->Paginator->sort('nombre')?></th>
<th scope="col"><?=$this->Paginator->sort('precio')?></th>
<th scope="col"><?=$this->Paginator->sort('tipo_id')?></th>
<th scope="col" class="actions">Acciones</th>
</tr>
</thead>
<tbody>
<?php foreach($productos as $producto):?>
<tr>
<td><?=$this->Number->format($producto->id)?></td>
<td><?=h($producto->nombre )?></td>
<td><?=$this->Number->format($producto->precio)?></td>
<td>

19
Curso PHP Medio Escuela de Administración Pública

<?=$producto->has('tipo') ? $this->Html->link($producto->tipo->nombre,
['controller'=>'Tipos', 'action'=>'view', $producto->tipo->id]):''?>
</td>
<td class="actions">
<?=$this->Html->link('Ver', ['action'=>'view', $producto->id])?>
<?=$this->Html->link('Editar', ['action'=>'edit', $producto->id])?>
<?=$this->Form->postLink('Eliminar', ['action'=>'delete', $producto->id],
['confirm'=>'¿Realmente desea eliminar?'])?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<div class="paginator">
<ul class="pagination">
<?=$this->Paginator->first('<< primero')?>
<?=$this->Paginator->prev('< anterior')?>
<?=$this->Paginator->numbers()?>
<?=$this->Paginator->next('siguiente >')?>
<?=$this->Paginator->last('último >>')?>
</ul>
<p><?= $this->Paginator->counter(['format'=>'Página {{page}} de {{pages}}, mostrando {{current}}
de {{count}}'])?></p>
</div>
</div>

Aunque aún no tenemos ningún registro para poder apreciarlo en el navegador, en esta vista podemos destacar
tres nuevos elementos.
El primero se refiere al nombre de la columna del campo relacionado. Antes de mostrar nada, pregunta si ese
campo tiene valor y a continuación muestra el nombre del tipo como un enlace que lleva a la vista (acción view())
de tipos.
Ese es el segundo elemento a destacar, es decir, la forma de mostrar el campo relacionado en cada fila de la tabla
de datos. Para ello usa un enlace con el ayudante HTML de CakePHP para que al hacer clic en él, accedamos a la
vista que muestra solo su información, sin posibilidad de editarla. Lo notable en este enlace es la forma de titular el
enlace con $producto->tipo->nombre, es decir, desde cada producto proporcionado por el controlador puede
acceder a los campos del modelo tipo relacionado.
El tercer elemento es simplemente la inclusión de dos elementos en el menú de la izquierda para acceder al
listado de tipos y crear un nuevo tipo desde esta vista.
Acceda con el navegador a http://localhost/catalogo/productos o haga clic en el menú Productos. El aspecto del
listado vacío de productos, a expensas de crear las vistas para añadir y editar es el de la figura 7.12.

Figura 7.12. Listado de productos.

Cree el archivo para añadir un nuevo producto en catalogo/src/Template/Productos/add.ctp, e incluya el código


del listado siguiente:

<nav class="large-3 medium-4 columns" id="actions-sidebar">


<ul class = "side-nav">
<li class="heading">Acciones</li>
<li><?=$this->Html->link('Productos', ['action'=>'index'])?></li>
<li><?=$this->Html->link('Tipos', ['controller'=>'Tipos', 'action'=>'index'])?></li>
<li><?=$this->Html->link('Nuevo Tipo', ['controller'=>'Tipos', 'action'=>'add'])?></li>
</ul>
</nav>

20
Curso PHP Medio Escuela de Administración Pública

<div class="productos form large-9 medium-8 columns content">


<?=$this->Form->create($producto)?>
<fieldset>
<legend>Nuevo Producto</legend>
<?php
echo $this->Form->control('nombre');
echo $this->Form->control('precio');
echo $this->Form->control('tipo_id', ['options'=>$tipos]);
?>
</fieldset>
<?=$this->Form->button('Crear')?>
<?=$this->Form->end()?>
</div>

Como puede observar, CakePHP nos descarga de la necesidad de crear por nosotros el listado desplegable de tipos
para relacionar con el nuevo producto. Simplemente es necesario indicar, utilizando el ayudante de formularios, que
desea mostrar el campo relacionado, suministrarle el array $tipos y CakePHP se encargará del resto.

Figura 7.13. Formulario con campo relacionado.

Cree varios productos y observe cómo el campo tipo relacionado aparece en el listado de productos (figura 7.14)
con su nombre y un enlace que le lleva a su formulario de edición.

Figura 7.14. Listado con registros relacionados.

El método view() de Productos para ver la información de un producto concreto es similar también a la vista
de tipo de producto con la diferencia del campo nombre de tipo a mostrar.

21
Curso PHP Medio Escuela de Administración Pública

<nav class="large-3 medium-4 columns" id="actions-sidebar">


<ul class="side-nav">
<li class="heading">Acciones</li>
<li><?=$this->Html->link('Editar', ['action'=>'edit', $producto->id])?></li>
<li><?=$this->Form->postLink('Eliminar', ['action'=>'delete', $producto->id],
['confirm'=>'¿Realmente desea eliminar?'])?> </li>
<li><?=$this->Html->link('Productos', ['action'=>'index'])?> </li>
<li><?=$this->Html->link('Nuevo Producto', ['action'=>'add'])?> </li>
<li><?=$this->Html->link('Tipos', ['controller'=>'Tipos', 'action'=>'index'])?> </li>
<li><?=$this->Html->link('Nuevo Tipo', ['controller'=>'Tipos', 'action'=> 'add'])?> </li>
</ul>
</nav>
<div class="productos view large-9 medium-8 columns content">
<h3><?=h($producto->id)?></h3>
<table class="vertical-table">
<tr>
<th scope="row">Nombre</th>
<td><?=h($producto->nombre)?></td>
</tr>
<tr>
<th scope="row">Tipo</th>
<td><?=$producto->has('tipo')?$this->Html->link($producto->tipo->nombre,
['controller'=>'Tipos', 'action'=>'view', $producto->tipo->id]):''?></td>
</tr>
<tr>
<th scope="row">Id</th>
<td><?=$this->Number->format($producto->id)?></td>
</tr>
<tr>
<th scope="row">Precio</th>
<td><?=$this->Number->format($producto->precio)?></td>
</tr>
</table>
</div>

Finalmente, debe permitir editar los productos. El cambio a realizar en el controlador es exactamente el mismo
que ha realizado en el método para añadir. Cree el archivo que le permitirá editar los registros en
catalogo/src/Template/Productos/edit.ctp con el siguiente contenido:

<nav class="large-3 medium-4 columns" id="actions-sidebar">


<ul class="side-nav">
<li class="heading">Acciones</li>
<li><?=$this->Form->postLink('Eliminar',
['action'=>'delete', $producto->id],
['confirm'=>'¿Realmente desea eliminar?']
)?>
</li>
<li><?=$this->Html->link('Productos', ['action'=>'index'])?></li>
<li><?=$this->Html->link('Tipos', ['controller'=>'Tipos', 'action'=>'index'])?></li>
<li><?=$this->Html->link('Nuevo Tipo', ['controller'=>'Tipos', 'action'=>'add'])?></li>
</ul>
</nav>
<div class="productos form large-9 medium-8 columns content">
<?=$this->Form->create($producto)?>
<fieldset>
<legend>Editar Producto</legend>
<?php
echo $this->Form->control('nombre');
echo $this->Form->control('precio');
echo $this->Form->control('tipo_id', ['options'=>$tipos]);
?>
</fieldset>
<?= $this->Form->button('Actualizar')?>
<?= $this->Form->end()?>
</div>

22
Curso PHP Medio Escuela de Administración Pública

Realmente, excepto el campo id, el resto es prácticamente idéntico al formulario para añadir productos. Sin
embargo, la peculiaridad de este formulario de edición es que al mostrar el producto, el tipo ya vendrá seleccionado
en la lista desplegable de tipos.

Figura 7.15. Formulario de edición de productos.

Es posible mostrar más información en la vista de los tipos de producto, como por ejemplo, los productos de un
tipo concreto. Así, al hacer clic en el enlace Ver de la lista de tipos, en la vista view.ctp aparecerá un listado de los
productos de ese tipo concreto. Para ello, incluya el siguiente código a continuación de la tabla en
catalogo/src/Template/Tipos/view.ctp:

<div class="related">
<h4>Productos Relacionados</h4>
<?php if(!empty($tipo->productos)):?>
<table cellpadding="0" cellspacing="0">
<tr>
<th scope="col">Id</th>
<th scope="col">Nombre</th>
<th scope="col">Precio</th>
<th scope="col" class="actions">Acciones</th>
</tr>
<?php foreach($tipo->productos as $productos):?>
<tr>
<td><?=h($productos->id)?></td>
<td><?=h($productos->nombre)?></td>
<td><?=h($productos->precio)?></td>
<td class="actions">
<?=$this->Html->link('Ver', ['controller'=>'Productos', 'action'=>'view', $productos->id])?>
<?=$this->Html->link('Editar',['controller'=>'Productos','action'=>'edit',$productos->id])?>
<?=$this->Form->postLink('Eliminar',
['controller'=>'Productos', 'action'=>'delete', $productos->id],
['confirm'=>'¿Realmente desea eliminar?'])?>
</td>
</tr>
<?php endforeach;?>
</table>
<?php endif;?>
</div>

Observe que es un listado similar al de index.ctp de cualquier módulo con la salvedad de que los productos que
aquí se muestran son los que tienen como categoría relacionada la que se está mostrando.
En el método view() del TiposController es necesario indicar que al obtener el registro que queremos mostrar,
incluya además la información de los productos relacionados usando el índice contain:
public function view($id = null)
{
$tipo = $this->Tipos->get($id, [
'contain' => ['Productos']
]);
$this->set('tipo', $tipo);
}

23
Curso PHP Medio Escuela de Administración Pública

En la figura 7.16 puede ver el resultado:

Figura 7.16. Productos relacionados al ver un tipo de producto.

7.7. Conclusión
Hoy en día, y no sólo sucede con PHP, es un riesgo y un esfuerzo considerable crear una aplicación sin utilizar
un framework de desarrollo que agilice el trabajo diario y facilite la mayoría de las utilidades y controles de
seguridad, MVC, pruebas unitarias, así como la integración con elementos de la interfaz de usuario (HTML, CSS,
javascript).
CakePHP es uno de los marcos de trabajo más utilizados, más estables y con una evolución continua muy
notable, mantenida por una comunidad muy viva. Le recomendamos su uso, aunque debe ampliar sus
conocimientos accediendo a la página oficial de CakePHP (http://www.cakephp.org). Si CakePHP no es de su
agrado o prefiere investigar otros frameworks, existen multitud de proyectos en PHP. Nosotros le aconsejamos
algunos ya maduros como Laravel (http://www.laravel.com) o Symfony (http://ww.symfony.com).

24

También podría gustarte