Hasta aquí hemos invertido tiempo en revisar el uso básico del ScaffoldController con KumbiaPHP creando nuestros CRUDs de forma rápida, modificando el estilo de las vistas (al añadir un nuevo conjunto de vistas que cargan mediante la variable de controlador $scaffold), y reemplazando comportamientos particulares para modificar el conjunto de datos (al reescribir el método index).
Nueva meta u objetivo usando ScaffoldController
Este post tiene por objetivo hacer un resumen de lo que ya hemos visto en las entregas anteriores, y sacar aún más partido al uso de scaffolding con nuestro framework, de tal manera que puedas aplicar los conceptos que se describen en otras situaciones y así buscar mantener el principio DRY: No te repitas.
En sí, el uso de Scaffolding es una declaración clara del principio DRY, pues lo usamos para no tener que copiar y pegar comportamientos que son inherentes a diferentes situaciones: como crear, actualizar, eliminar y listar registros de una tabla (a modo de ejemplo, podríamos crear controladores scaffolding o heredables para otras tareas que no sean siempre la gestión de datos en tablas).
A modo de añadir más fuerza al principio, los frameworks de desarrollo web se han creado basándose primariamente en dicha idea: escribir lo necesario, evitando repetir comportamientos, y por ende se disminuye el número de líneas de código, el nivel de errores y, por ende, el tiempo de desarrollo y mantenimiento de los sistemas o aplicaciones creados con ellos.
Por eso es que existe una clasificación de carpetas: para controladores, modelos, vistas, ayudantes (helpers), librerías particulares, y librerías externas (vendors).
Si necesita comprender más los conceptos básicos de nuestro framework puede ver Kumbia Essentials.
Manos a la obra
Como ya se mencionó, un scaffolding es una estrategia para no repetir código que se usa en labores comunes. Hasta aquí lo hemos usado para tener un CRUD de la tabla que representa las categorías, y también nos ha permitido sobrescribir la forma en que hacemos la presentación de la acción index (listar los registros de la tabla).
Ahora iremos un poco más lejos
- Modificaremos la vista particular de la acción index.
- Sobrescribiremos nuestro controlador para modificar el comportamiento al guardar y actualizar los registros.
El cliente nos pide modificar la visualización de la lista de categorías para quitar de ella los atributos de fecha y renombrar el atributo nombre y categorías por Nombre Categoría y Categoría Padre. También nos solicita modificar el comportamiento de la acción crear para que el formulario aparezca limpio para agregar nuevamente, en vez de viajar a index una vez enviado el formulario.
De igual forma debe hacerse para que la acción editar recargue el formulario modificado en vez de viajar a index. Iremos de lo fácil a lo menos fácil.
Modificaremos el comportamiento de crear, y editar en nuestro controlador.
Podemos mirar el método crear desde ScaffoldController (app/libs/scaffold_controller.php) y mejorarlo para nuestro controlador de categorias (app/controllers/categorias_controller.php)
Una propuesta podría ser como la que se presenta a continuación:
<span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">crear</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span> Input<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">hasPost</span><span class="token punctuation">(</span><span class="token string">'Categorias'</span><span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token variable">$categoria</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Categorias</span><span class="token punctuation">(</span>Input<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">post</span><span class="token punctuation">(</span><span class="token string">'Categorias'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">//comprobar si se puede realizar la creacion del elemento</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span> <span class="token operator">!</span><span class="token variable">$categoria</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">save</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span>
Flash<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">error</span><span class="token punctuation">(</span><span class="token string">'Falló Operación'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">//hacer persistente el objeto con los datos enviados</span>
<span class="token variable">$this</span><span class="token operator">-</span><span class="token operator">></span><span class="token property">Categorias</span> <span class="token operator">=</span> <span class="token variable">$categoria</span><span class="token punctuation">;</span>
Input<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">delete</span><span class="token punctuation">(</span><span class="token string">'Categorias'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token variable">$this</span><span class="token operator">-</span><span class="token operator">></span><span class="token property">Categorias</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Categorias</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
Esta propuesta elimina la redirección a index, y limpiar el contenido de Input::post(‘modelo’) para que el formulario permanezca en blanco y así nos permita agregar un nuevo elemento de forma inmediata.
Repetiremos el procedimiento en el método editar. Iremos al controlador ScaffoldController, y desde él mejoraremos el método editar, pegándolo bajo el método crear del controlador de categorías. La propuesta para editar debería ser algo como la siguiente figura:
<span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">editar</span><span class="token punctuation">(</span><span class="token variable">$id</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
View<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">select</span><span class="token punctuation">(</span><span class="token string">'crear'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//usamos la misma vista que crear</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span> Input<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">hasPost</span><span class="token punctuation">(</span><span class="token string">'Categorias'</span><span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token variable">$categoria</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Categorias</span><span class="token punctuation">;</span>
<span class="token comment">//comprobar si se puede realizar la actualización del elemento</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span> <span class="token operator">!</span><span class="token variable">$categoria</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">update</span><span class="token punctuation">(</span>Input<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">post</span><span class="token punctuation">(</span><span class="token string">'Categorias'</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span>
Flash<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">error</span><span class="token punctuation">(</span><span class="token string">'Falló Operación'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">//hacer persistente el objeto con los datos del formulario</span>
<span class="token variable">$this</span><span class="token operator">-</span><span class="token operator">></span><span class="token property">Categorias</span> <span class="token operator">=</span> <span class="token variable">$categoria</span><span class="token punctuation">;</span>
Input<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">delete</span><span class="token punctuation">(</span><span class="token string">'Categorias'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token comment">//aplicar autocarga del objeto para comenzar/continuar la edición</span>
<span class="token variable">$this</span><span class="token operator">-</span><span class="token operator">></span><span class="token property">Categorias</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">Categorias</span><span class="token punctuation">)</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">find</span><span class="token punctuation">(</span><span class="token punctuation">(</span>int<span class="token punctuation">)</span><span class="token variable">$id</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
Modificando vistas
Modificaremos la vista index estándar. Para ello crearemos una nueva vista index dentro de app/views/categorias. El archivo debe ser llamado como index.phtml.
La estructura de archivos del proyecto debería verse así:
Copiaremos el contenido de la vista index del scaffolding, la que está en app/views/_shared/scaffolds/kumbia/index.phtml
Quitaremos los atributos de fecha y renombraremos las columnas de nombre y de categoría padre. La vista nos debería quedar más o menos así:
<span class="token operator"><</span>div id<span class="token operator">=</span><span class="token string">"scaffold"</span><span class="token operator">></span>
<span class="token delimiter"><?php</span> View<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">content</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token delimiter">?></span>
<span class="token operator"><</span>h1<span class="token operator">></span><span class="token delimiter"><?</span><span class="token operator">=</span> <span class="token function">ucwords</span><span class="token punctuation">(</span><span class="token string">"$model"</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token string">": <span>$action_name</span>"</span> <span class="token delimiter">?></span><span class="token operator"><</span><span class="token operator">/</span>h1<span class="token operator">></span>
<span class="token operator"><</span>div <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"actions"</span><span class="token operator">></span>
<span class="token delimiter"><?</span><span class="token operator">=</span> Html<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">linkAction</span><span class="token punctuation">(</span><span class="token string">"crear/"</span><span class="token punctuation">,</span> <span class="token string">'Crear registro'</span><span class="token punctuation">,</span> <span class="token string">'class="btn btn-primary"'</span><span class="token punctuation">)</span><span class="token delimiter">?></span>
<span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span>
<span class="token delimiter"><?php</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">isset</span><span class="token punctuation">(</span><span class="token variable">$data</span><span class="token operator">-</span><span class="token operator">></span><span class="token property">items</span><span class="token punctuation">)</span> <span class="token operator">&&</span> <span class="token punctuation">(</span><span class="token function">count</span><span class="token punctuation">(</span><span class="token variable">$data</span><span class="token operator">-</span><span class="token operator">></span><span class="token property">items</span><span class="token punctuation">)</span> <span class="token operator">></span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">:</span> <span class="token delimiter">?></span>
<span class="token operator"><</span>table <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"t"</span><span class="token operator">></span>
<span class="token operator"><</span>thead<span class="token operator">></span><span class="token operator"><</span>tr<span class="token operator">></span>
<span class="token operator"><</span>th<span class="token operator">></span>Id<span class="token operator"><</span><span class="token operator">/</span>id<span class="token operator">></span>
<span class="token operator"><</span>th<span class="token operator">></span>Nombre<span class="token operator"><</span><span class="token operator">/</span>id<span class="token operator">></span>
<span class="token operator"><</span>th<span class="token operator">></span>Categoria Padre<span class="token operator"><</span><span class="token operator">/</span>id<span class="token operator">></span>
<span class="token operator"><</span>th<span class="token operator">></span>Acciones<span class="token operator"><</span><span class="token operator">/</span>th<span class="token operator">></span>
<span class="token operator"><</span><span class="token operator">/</span>tr<span class="token operator">></span><span class="token operator"><</span><span class="token operator">/</span>thead<span class="token operator">></span>
<span class="token operator"><</span>tbody<span class="token operator">></span>
<span class="token delimiter"><?php</span> <span class="token keyword">foreach</span> <span class="token punctuation">(</span><span class="token variable">$data</span><span class="token operator">-</span><span class="token operator">></span><span class="token property">items</span> <span class="token keyword">as</span> <span class="token variable">$item</span><span class="token punctuation">)</span> <span class="token punctuation">:</span> <span class="token delimiter">?></span>
<span class="token operator"><</span>tr<span class="token operator">></span>
<span class="token operator"><</span>td<span class="token operator">></span><span class="token delimiter"><?</span><span class="token operator">=</span> <span class="token function">h</span><span class="token punctuation">(</span><span class="token variable">$item</span><span class="token operator">-</span><span class="token operator">></span><span class="token property">id</span><span class="token punctuation">)</span><span class="token delimiter">?></span><span class="token operator"><</span><span class="token operator">/</span>td<span class="token operator">></span>
<span class="token operator"><</span>td<span class="token operator">></span><span class="token delimiter"><?</span><span class="token operator">=</span> <span class="token function">h</span><span class="token punctuation">(</span><span class="token variable">$item</span><span class="token operator">-</span><span class="token operator">></span><span class="token property">nombre</span><span class="token punctuation">)</span><span class="token delimiter">?></span><span class="token operator"><</span><span class="token operator">/</span>td<span class="token operator">></span>
<span class="token operator"><</span>td<span class="token operator">></span><span class="token delimiter"><?</span><span class="token operator">=</span> <span class="token function">h</span><span class="token punctuation">(</span><span class="token variable">$item</span><span class="token operator">-</span><span class="token operator">></span><span class="token property">categorias_id</span><span class="token punctuation">)</span><span class="token delimiter">?></span><span class="token operator"><</span><span class="token operator">/</span>td<span class="token operator">></span>
<span class="token operator"><</span>td<span class="token operator">></span><span class="token delimiter"><?</span><span class="token operator">=</span> Html<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">linkAction</span><span class="token punctuation">(</span><span class="token string">"ver/$item->id"</span><span class="token punctuation">,</span> <span class="token string">'Ver'</span><span class="token punctuation">)</span><span class="token delimiter">?></span> <span class="token operator">|</span>
<span class="token delimiter"><?</span><span class="token operator">=</span> Html<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">linkAction</span><span class="token punctuation">(</span><span class="token string">"editar/$item->id"</span><span class="token punctuation">,</span> <span class="token string">'Editar'</span><span class="token punctuation">)</span><span class="token delimiter">?></span> <span class="token operator">|</span>
<span class="token delimiter"><?</span><span class="token operator">=</span> Html<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">linkAction</span><span class="token punctuation">(</span><span class="token string">"borrar/$item->id"</span><span class="token punctuation">,</span> <span class="token string">'Borrar'</span><span class="token punctuation">,</span> <span class="token string">'onclick="return confirm(\'¿Está seguro?\')"'</span><span class="token punctuation">)</span> <span class="token delimiter">?></span>
<span class="token operator"><</span><span class="token operator">/</span>td<span class="token operator">></span>
<span class="token operator"><</span><span class="token operator">/</span>tr<span class="token operator">></span>
<span class="token delimiter"><?php</span> <span class="token keyword">endforeach</span><span class="token delimiter">?></span>
<span class="token operator"><</span><span class="token operator">/</span>tbody<span class="token operator">></span>
<span class="token operator"><</span><span class="token operator">/</span>table<span class="token operator">></span>
<span class="token delimiter"><?php</span> View<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">partial</span><span class="token punctuation">(</span><span class="token string">'paginators/digg'</span><span class="token punctuation">,</span> <span class="token boolean">false</span><span class="token punctuation">,</span> <span class="token keyword">array</span><span class="token punctuation">(</span><span class="token string">'page'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token variable">$data</span> <span class="token punctuation">,</span><span class="token string">'url'</span> <span class="token operator">=</span><span class="token operator">></span> Router<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">'controller_path'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token string">'/index'</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token delimiter">?></span>
<span class="token delimiter"><?php</span> <span class="token keyword">else</span> <span class="token punctuation">:</span> <span class="token comment">// Si no hay items?></span>
<span class="token operator"><</span>h2<span class="token operator">></span>No hay ningún registro<span class="token operator"><</span><span class="token operator">/</span>h2<span class="token operator">></span>
<span class="token delimiter"><?php</span> <span class="token keyword">endif</span> <span class="token delimiter">?></span>
<span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span>
Al ejecutar el acceso a la vista deberíamos lograr algo similar a la siguiente figura:
Pero nuestro cliente necesita que la categoría padre quede expresada con el nombre, no con su código de referencia.
Entonces, lo que haremos finalmente es modificar el método index y el modelo que enlaza la tabla categorías de la siguiente forma, comenzando por el modelo (app/models/categorias.php):
<span class="token delimiter"><?php</span>
<span class="token keyword">class</span> <span class="token class-name">Categorias</span> <span class="token keyword">extends</span> <span class="token class-name">ActiveRecord</span>
<span class="token punctuation">{</span>
<span class="token keyword">function</span> <span class="token function">getCategorias</span><span class="token punctuation">(</span><span class="token variable">$page</span> <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token keyword">return</span> <span class="token variable">$this</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">paginate</span><span class="token punctuation">(</span>
<span class="token string">'columns: categorias.id, categorias.nombre, cat.nombre as categorias_id'</span><span class="token punctuation">,</span>
<span class="token string">'join: left outer join categorias cat on categorias.categorias_id = cat.id'</span><span class="token punctuation">,</span>
<span class="token string">"page: $page"</span><span class="token punctuation">,</span> <span class="token string">'order: categorias.id desc'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
Modificamos la consulta para dotar a la paginación de la referencia al nombre de la categoría padre usando los parámetros columns y join.
En nuestro controlador de categorías modificaremos el método index para que pueda usar esta nueva paginación, quedando el resultado como se ve a continuación:
<span class="token delimiter"><?php</span>
<span class="token keyword">class</span> <span class="token class-name">CategoriasController</span> <span class="token keyword">extends</span> <span class="token class-name">ScaffoldController</span>
<span class="token punctuation">{</span>
<span class="token keyword">public</span> <span class="token variable">$model</span> <span class="token operator">=</span> <span class="token string">'Categorias'</span><span class="token punctuation">;</span>
<span class="token keyword">public</span> <span class="token variable">$scaffold</span> <span class="token operator">=</span> <span class="token string">'skeleton'</span><span class="token punctuation">;</span>
<span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">index</span><span class="token punctuation">(</span><span class="token variable">$page</span><span class="token operator">=</span><span class="token number">1</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token variable">$this</span><span class="token operator">-</span><span class="token operator">></span><span class="token property">data</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">Categorias</span><span class="token punctuation">)</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">getCategorias</span><span class="token punctuation">(</span><span class="token variable">$page</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
Finalmente el resultado de la vista index se verá como lo presenta la siguiente figura:
Hasta aquí llega la publicación dedicada al tema del scaffolding, pero antes de terminar haremos un resumen hasta aquí.
Resumen
En este episodio hemos modificado el comportamiento del método crear sobrescribiendo el método base del controlador Scaffold, también hemos modificado el comportamiento para editar, y para index.
Hemos agregado una vista personalizada para la acción index, y aún así seguimos usando ScaffoldController.
Sólo nos restará por tarea crear nuestro propio Scaffold para poder mantener el principio DRY usando los comportamientos sin redirección para el método crear y editar.
Si te perdiste los otros post de esta serie, puedes leerlos en los siguientes enlaces:
Scaffolding para CRUD (ABM) sencillos (y no tanto) – primera parteparte 1
ScaffoldController: Modificando comportamientos y contenidos
Un abrazo cordial a nuestros colegas y a los que han de venir,