Mostrando entradas con la etiqueta generalidades. Mostrar todas las entradas
Mostrando entradas con la etiqueta generalidades. Mostrar todas las entradas

sábado, 2 de enero de 2010

Programar en inglés

Hace tiempo alguien preguntó en el grupo google de cakephp en español acerca de la costumbre de programar en inglés y hace un par de días volví a caer en este post de CakeBaker sobre el tema.

Bien, primero habría que explicar lo que entiendo por "programar en inglés". Para empezar, PHP, como tantos otros lenguajes de programación, es una especie de dialecto del inglés. Por programar en inglés, quiero decir usar este idioma para nombres de variables, clases, funciones, comentarios e incluso para los textos iniciales de la aplicación.

¿Y qué razones tendríamos para hacer esto?

Hay algunas bastante prácticas, como las siguientes:


  • Siendo PHP un lenguaje basado en el inglés, evitamos mezclar idiomas, lo que hace más fácil la lectura del código.
  • Es más fácil compartir código con otros programadores de todo el mundo. Y en consecuencia:
  • Es más fácil que otros programadores te puedan ayudar en foros y grupos de correo, etc.
  • En CakePHP, específicamente, te puedes evitar algunos problemas con el Inflector cuando intentas añadir reglas para dar soporte a español y otros idiomas y que interfieren con las establecidas por defecto para el inglés.
  • En bastantes casos, la jerga técnica en inglés es más completa, expresiva y precisa que en español, por lo que los comentarios serán mucho más informativos. 
Y, la más importante:
  • Programando en inglés, pareces mejor programador de lo que eres ;-)
echo "Happy new year!"

miércoles, 18 de noviembre de 2009

Cosas que hago mal

Dejando aparte errores de sintaxis y algunos fallos lógicos, más o menos normales, he estado pensando que hago algunas cosas mal en mi "estrategia" (por decir algo) de desarrollo.

Una de las principales es que tiendo a centrarme demasiado en lo periférico, con lo cual avanzo relativamente poco en mis tareas, aunque teclee innumerables líneas de código.

Por ejemplo, llevo unos meses trabajando (un poco irregularmente) en el desarrollo de un proyecto que, en principio, será un CMS y la base para una aplicación de Intranet para el colegio en el que trabajo.

Pues bien, he dejado la parte de CMS para el final, y mientras tanto he estado desarrollando módulos auxiliares que, si bien son importantes, no son el objetivo principal de la cuestión. Sólo últimamente he empezado a trabajar en el módulo de contenidos, el cual, en muchos aspectos debería haber sido el primero (o uno de los primeros, ya que también me resulta muy importante el de gestión de usuarios y autorización).

Con todo, no he estado parado, sino que he desarrollado una serie de módulos que aportan gran cantidad de funcionalidad: comentarios, etiquetas, licencias, colecciones, uploads..., pero todavía no hay una aplicación real que esté funcionando. Lo que resulta frustrante.

La cuestión es que desde un punto de vista más "pragmático" lo lógico hubiera sido empezar a desarrollar la parte de gestion de contenidos (dejaré fuera de la cuestión la parte de acceso, que estoy revisitando ahora mismo) y seguramente estos otros elementos hubieran ido surgiendo de forma natural, aportando funcionalidad "avanzada" a la básica.

Mi CMS se estructura en torno a los modelos Channels e Items. Los Channels son canales, que pueden ser blogs, podcasts, photologs o similares. Los Items son los posts, episodios, etc. Si conoces la estructura de los RSS entenderás en qué me he inspirado.

La idea que intento explicar es que probablemente es más eficaz y mejor práctica comenzar por desarrollar una funcionalidad básica "gruesa", sin preocuparse de detalles. Es decir, en este caso, poner a funcionar mis channels e items, perfeccionando luego, de forma iterativa, el sistema añadiendo nuevas prestaciones.

Probablemente eso ayudaría también a mantener una buena motivación a lo largo del trabajo, cosa que en algunos momentos hecho de menos.

domingo, 13 de septiembre de 2009

Unit-testing y yo

Hace unos pocos posts comenté que una de las estrategias que estaba adopotando en mi desarrollo, junto a la modularización por plugins, es el unit-test y el desarrollo dirigido por tests.

Como programador autodidacta uno de los grandes problemas que encuentro es aprender de una forma sistemática y fundamentada. Uno no dispone de la estructuración que una formación más reglada te puede proporcionar. De este modo, temas como los patrones de programación o el uso de los test en el proceso de desarrollo no te resultan evidentes al principio, a veces, ni siquiera comprensibles.

Bueno, que me enrollo. Yo quería hablar de cómo llegué al punto en el que efectivamente uso los tests para probar mi software y cada vez más sigo una metodología dirigida por ellos. Pero quizá convenga empezar por explicar que es eso de los tests.

Test: probando el software

Al principio uno escribe trozos de código y ejecuta el programa para ver qué pasa. Es una manera básica de probar un software. De hecho, eso es básicamente un test de software: ejecutarlo y ver si ofrece los resultados esperados.

Cuando empecé con CakePHP, gracias a que proporciona una base completa para una aplicación, hacía más o menos eso mismo.

Estos tests "a ojímetro" no funcionan tan mal en situaciones sencillas, pero en cuanto las cosas empiezan a complicarse se vuelven ineficientes en progresión geométrica. En un momento dado resultan inútiles.

Cuando aparece un error en la aplicación, puede que haya fallado dentro de una función o método, pero es muy posible que la causa del error esté muy lejos en la pila de llamadas. Es decir, que el error real esté en un lugar y con la información disponible no tengamos forma de encontrarlo sin revisar toda la aplicación.

Eso sin mencionar la dificultad, por ejemplo, de probar los múltiples escenarios en que una aplicación puede funcionar. ¿De qué manera puedes saber que has probado el efecto de introducir ciertos datos de diferentes maneras? ¿Alguno de ellos puede generar un error?

Y, finalmente, si realizas un cambio de código, ¿cómo vuelves a probar todo otra vez para asegurarte de que el cambio no tiene efectos indeseados en otra parte?

Se hace necesario utilizar una metodología más sistemática y eficaz, que permita replicar las pruebas las veces que haga falta en multiplicidad de condiciones. Aquí es donde entra el Unit-testing.

Unit-rest: prueba de unidades de software

Como dice el título del apartado, el unit-test es una metodología de prueba de software que se basa en la prueba aislada de las unidades mínimas en que podemos dividir nuestro código. En el caso de CakePHP, que es un framework orientado a objetos, esas unidades son los métodos de las diferentes clases que componen la aplicación.

Por otro lado, los test serían programas que se encargan de llamar a las distintas unidades con diferentes condiciones (parámetros que se pasan, constantes globales, etc.) y comparar el resultado que ofrecen con los resultados que esperamos. Por ejemplo, si una función calcula el doble de un número, el test consistiría en algo así como:

$resultado = dobleDe(100);
    $esperado = 200;
if($resultado == $esperado) {
    echo 'OK';
} else {
    echo 'algo falla en dobleDe';
}

Al ser un programa podemos repetirlo cuantas veces queramos, en especial si hacemos algún cambio en la función dobleDe(), lo que nos diría si nuesto cambio o refactor está afectando al funcionamiento del código.

También podríamos probar multitud de valores para asegurarnos de que la función devuelve los valores correctos, sobre todo en ciertos puntos críticos. Por ejemplo, una función para calcular el precio con descuento por volumen en una tienda podría tener los intervalos:

Unidades                  descuento
menos de 10 ud.       0 %
entre 10 y 20 ud       3% de descuento
más de 20 ud            5% de descuento

Aquí tendríamos que probar al menos los siguientes valores de unidades:

<10 =10 >20 y <20 =20 >20

Es decir, tendríamos que escribir al menos 5 test variando las unidades de producto para ver si la función nos devuelve el precio correcto para una combinación de producto y cantidades.

Ayudas al Unit-Test

Por supuesto, escribir los tests "a pelo" es un trabajo considerable. Para ayudar en la tarea existen bibliotecas como SimpleTest, en la cual se basa el Test Suite de CakePHP.

En conjunto la Test Suite nos proporciona un entorno para probar las clases que escribimos para nuestra aplicación, garantizándonos el mínimo de funcionalidad que necesitamos para que nuestros modelos, controladores y vistas puedan ser probados, así como funciones específicas para hacer los tests.

Un concepto básico son las aserciones o asserts. Se trata de afirmaciones que hacemos sobre el resultado de una unidad. Por ejemplo, que el resultado va a ser igual a cierto valor, que será cierto o falso, o que se coincidirá con una determinada expresión regular.

CakePHP tiene una clase CakeTestCase que incorpora la mayoría de asserts que podemos necesitar, así, podremos escribir un test como el siguiente:

$result = $this->Post->find('count');
$this->assertEqual($result, 5):

Que básicamente quiere decir que el find('count') debería encontrar 5 registros en la base de datos que estamos usando de prueba.

Otra ayuda importante son los Mock Objects. Estos objetos nos permiten imitar el comportamiento de objetos de nuestra aplicación, pero sin que ejecuten su código real, sino que ofrecen la misma intefaz y podemos programarlos para que devuelvan ciertos resultados que nos interesen.

Es decir, que si el método que estamos probando llama a un objeto "mockeado", no se ejecutará el código del objeto original, sino que el "mock" nos devolverá el valor que le hemos configurado que devuelva.

Eso nos permite aislar el código que estamos probando del resto de la aplicación, lo que hace más fiable el test (ejecuta sólo el código que probamos) y nos permite jugar con diferentes escenarios.

Un ejemplo típico es hacer un Mock del EmailComponent. Puede que en nuestra máquina de test no podamos enviar correo usando el EmailComponent, pero haciendo un Mock podemos simular que lo ha enviado y basarnos en eso para probar una parte de la aplicación. O también podemos probar la condición de que no funciona y ver cómo la supera nuestra aplicación.

También es posible probar condiciones de error. Es posible utilizar la función expectError para detectar que nuestro código produce un error. Por ejemplo, cuando lanzas un error desde el código si los datos que llegan a un método son inválidos.

La asserts, además, permiten a la Test Suite realizar algunas estadísticas con tus test. De este modo, puedes tener un número de tests sobre una clase y saber cuántos pasan, cuántos fallan y si se han provocado excepciones.

Desarrollo dirigido por tests

El desarrollo dirigido por tests es una metodología en que usas el Unit-testing como base para desarrollar tus aplicaciones. Se trata de escribir los tests antes que el código de las unidades.

¿Cómo?

Sí, al principio me costó mucho entender esta idea, que ahora me parece de lo más evidente.

En el fondo, escribir un test para una unidad de software es definir de una manera formal sus especificaciones e interfaz: qué parámetros debe recibir y qué resultados ha de proporcionar y en qué formato.

Esto puede hacerse antes de escribir el código, por supuesto. Tú sabes lo que quieres que haga un método antes de escribirlo. En realidad, en un equipo de desarrollo, ni siquiera tendría que ser la misma persona la que prepara los test y la que realiza el código.

Preparar el test te hace pensar muy a fondo en la interfaz del método. Y escribir código para cumplir el test te obliga a estar muy enfocado en lo que estás haciendo. Y, sobre todo, te proporciona una red de seguridad para el futuro.

Tests y refactor

En un momento dado te plantearás refactorizar el código. Los tests te ayudan a garantizar que no se rompe nada. Es genial, en serio: incorporas unas modificaciones y pruebas, si falla, revisas de nuevo y reescribes, vuelves a probar, y así sucesivamente hasta que vuelves a pasar el test. Y, si no, puedes volver a la revisión anterior que sí funcionaba.

Si se trata de añadir funcionalidades nuevas, los test también te ayudan. Por un lado, los tests originales te garantizan que el nuevo código no rompe la funcionalidad original. Por otro lado, debes añadir tests que prueben las nuevas características.

Unit-test y calidad de vida

Pues mejora mucho. Me costó llegar a realizar tests para las diferentes clases. Tiene su complicación probar un controlador por ejemplo, o un behavior. Sin embargo, superadas esas dificultades (lo que a su vez me permitió aprender mucho acerca de cómo funciona CakePHP), el resultado no puede ser mejor.

Mi código está mejor escrito y más pensado. Mi trabajo es más focalizado en objetivos concretos. Además, debido a que con frecuencia tengo que interrumpir el desarrollo para dedicarme a otras actividades, me resulta mucho más fácil retomar el trabajo después de un tiempo. También me permite, por ejemplo, dedicar ratos sueltos a resolver pequeños problemas y avanzar en los proyectos, tomando algún problema detectado o algún test fallido y viendo cómo resolverlo.

Al poder trabajar con partes aisladas, no tienes miedo a romper la aplicación y tener muchos frentes abiertos. Te centras en una tarea y la resuelves, luego otra y luego otra.

La inversión de tiempo en aprender a usar los test y en crearlos realmente merece la pena.

Saber si haces buenos tests

El Test Suite incluye soporte para analizar la cobertura de código de tus test mediante Xdebug. Me costó un poco preparar mi sistema para poder utilizarlo y ha sido una especie de revelación cuando lo conseguí. Además, Xdebug mejora la información que te devuelve la aplicación cuando hay errores de PHP, mostrándote la pila de llamadas y diversa información.

La cobertura de código te indica qué porcentaje del código probado es ejecutado en realidad. Te ayuda a descubrir partes del código que no se ejecutan, condiciones que no has probado y otros muchos detalles, pues te muestra los fragmentos no ejecutados.

Al disponer de esta información es más fácil hacer tests para todo tipo de condiciones, o saber si tienes que crear nuevos tests aunque cubras el 100% del código con los que tienes, al añadir nuevas funcionalidades o hacer refactor de un método.

Los límites de los test

Los test no son la solución para garantizar un programa perfectamente libre de errores. Los test te informan de que una unidad de software hace lo que esperas que haga, suponiendo que has cubierto todos los escenarios posibles con los tests.

Sin embargo, la tranquilidad y seguridad que te proporcionan, la focalización que te aportan y la objetividad y claridad que te dan a la hora de definir tus tareas de programación, tienen un valor que compensan claramente estos límites.

jueves, 16 de julio de 2009

Usando plugins para modularizar aplicaciones

Después de una temporada de alejamiento "forzoso" del desarrollo con CakePHP he decidido afrontar un par de nuevos proyectos utilizando nuevos enfoques en mi metodología de trabajo.

El primero de ellos es la modularización del código en base a plugins. El otro es la introducción del unit-testing y del desarrollo dirigido por tests.

Hoy voy a escribir sobre los plugins, por qué me he decidido a replantear mis aplicaciones con ellos y qué detalles he descubierto que hay que tener en cuenta para hacerlos funcionar bien.

Modularizar con plugins

Una queja habitual de la estructura de CakePHP es que no se pueden organizar los modelos, controladores y vistas de forma significativa, pues todos los modelos tienen que ir juntos en su carpeta, al igual que todos los controladores, etc.

Los plugins nos permiten abordar ese problema ya que con ellos agrupamos los modelos, controladores y vistas (y sus extensiones) que guarden alguna relación. Cada plugin es como una mini-aplicación CakePHP y en su interior reproduce su estructura general.

Los plugins pueden ser bastante autocontenidos, de modo que es sencillo reutilizarlos en nuevos proyectos. Supongamos, por ejemplo, que hemos creado un sistema de gestión de usuarios y grupos con el que nos encontramos muy a gusto. Si lo tenemos como plugin, tan sólo debemos copiar la carpeta que lo contiene en la nueva aplicación para disponer de toda la funcionalidad.

En cambio, si no está en forma de plugin, es más fácil perder la pista de algún archivo a la hora de copiarlo, en especial si se manejan varios modelos, etc.

Mi objetivo al usar plugins

Pues mi objetivo es muy sencillo. Se trata de tener "piezas" de código relativamente independientes que puedan servir para montar una diversidad de aplicaciones. De este modo dispondré de una especie de "superframework" con módulos reutilizables. Cada aplicación o proyecto tendría partes propias, y partes comunes. De este modo, al avanzar en uno, estoy contribuyendo a otros. Y, por otro lado, me puedo concentrar en el núcleo de una aplicación específica.

Y en caso de trabajar en equipo, es fácil distribuir el trabajo.

Por otra parte, los plugins son también una buena manera de crear bibliotecas de behaviors, components o helpers, que puedan reutilizarse en varios proyectos. Lo bueno, es que puedes agruparlos por algún criterio útil. Por, ejemplo, una biblioteca de helpers relacionados con la interfaz de usuario. O un módulo con un behavior para añadir comentarios a cualquier otro objeto, que incluye la gestión de los mismos.

Creando un plugin

Lo mejor es utilizar cake bake para crear la estructura básica del plugin.

cake bake plugin nombre_del_plugin

Lo más importante a tener en cuenta es que los modelos y controladores dentro de un plugin no deberían descender de AppModel o de AppController, sino indirectamente, a través de AppPluginModel o AppPluginController. Cake Bake se ocupa de eso por nosotros.

También podemos generar modelos y controladores básicos mediante cake bake

cake bake plugin NombreDelPlugin model NombreDelModelo

Accediendo a las acciones de un plugin

Las URL que nos llevan a un plugin son las típicas de cake prefijadas con el nombre del plugin:

/plugin/controller/action/param

Como, por ejemplo:

/content/posts/edit/first_post
/admin/tickets/tickets/edit/123

Como puedes ver, también es posible usar el admin routing con plugins.

Por otra parte, es perfectamente posible llamar a las acciones de los plugins con RequestAction.

Creando URL

En el caso de que crees las URL para link o redirect con el formato array, debes incluir la clave 'plugin' con valor true.

array('admin' => true, 'plugin' => true, 'action' => 'index')

Asociaciones con modelos dentro de un plugin

Cuando necesitamos asociar un modelo con otro que está en un plugin, tenemos que indicárselo. Puede ser en la forma simple:

var $hasMany = array('Plugin.Modelo');

O si tienes que especificar parámetros de la asociación, incluyendo la clave

'className' = 'Plugin.Modelo',

Recuerda que ésta, y las demás indicaciones, se aplican incluso cuando los modelos asociados están en el mismo plugin.

Usando behaviors, components o helper de un plugin

Decláralos como siempre, añadiendo antes el nombre del plugin, como en este ejemplo:

var $actsAs = array('Comments.Commentable);

Usando elementos de un plugin

Entre los parámetros que pasamos al elemento, hay que indicar la clave 'plugin' y el nombre del plugin.

element('ejemplo', array('plugin' => 'el_plugin')); ?>

Importando modelos o controladores

Hay que indicar el nombre del plugin, para que importe el objeto correcto.

App::import('Model', 'Plugin.Model');
App::import('Controller', 'Plugin.Controller');

Testing con plugins

Algunas cosas que he observado que es necesario tener en cuenta son:

Los controladores que usamos para testar deben incluir el parámetro plugin (gracias a Mark Story)

class TestPostsController extends PostsController {
var $plugin = 'Content';
}

Y hay que tener en cuenta el apartado anterior para importar los objetos que estén en plugins.

La declaración de las fixtures, también lo tiene en cuenta:

var $fixtures = array('plugin.content.post);

El test suite de CakePHP trata cada plugin aisladamente, lo que resulta muy cómodo y casi te evita tener que hacer grupos de tests por funcionalidad u otro criterio.

Shells

Puedes empaquetar shells en plugins. Crea la ruta vendors/shell dentro de la carpeta del plugin que se trate y pon ahí los shells que desees. La utilidad de línea de comandos cake sabrá acceder a ellos.

Por supuesto, hay más información en el Cookbook.

martes, 7 de octubre de 2008

La encuesta

Bueno, los resultados de la (nada) científica encuesta (pocos votos) evidencian que si se pasan por aquí los teóricos del patrón MVC nos corren a gorrazos y nos echan al pilón por herejes.

Ha salido ganadora la opción de dejar a la vista lidiar con las diferencias en estructuras de datos.

Yo soy más partidario de la segunda opción, hacer que el controller elija la vista aunque él mismo obtenga estructuras de datos un poco diferentes.

La otra opción muchas veces produce métodos demasiado parecidos que atentan bastante contra el principio DRY ("no te repitas"). Tampoco es que haya que respetar estos principios hasta la última consecuencia (en último término te bloquean), sino tomarlo como guía. En el ejemplo que yo proponía adopté finalmente esta opción.

A medida que progreso en el aprendizaje de CakePHP me voy dando cuenta de que trato de eliminar código PHP de las vistas. Es decir, cada vez creo más en las vistas "tontas", que sólo saben recibir datos y mostrarlos, de modo que el código PHP que haya en ellas sólo sirva para convertir formatos o preparar información para helpers o elements. Pero casa vista debe saber recibir un tipo de datos y mostrarlo de una única manera.

Al Controller intento dejarle algo más de inteligencia, pero no demasiada. En ocasiones, como es el caso que proponía, las diferencias son lo bastante pequeñas como para darle al Controller cierto margen de decisión, en este caso, escoger la View, o incluso el layout, en función de ciertos parámetros.

miércoles, 17 de septiembre de 2008

Encuesta chorra (o no)

Aprovechando que el Blogger ahora admite encuestas (ver arriba), se me ha ocurrido la siguiente pregunta. Supongamos que tienes un sistema de blogs con los modelos Blog y Post, asociados de modo que Blog hasMany Post.

Ahora supongamos que la raíz del sitio (/) saca una lista de todos los Post recientes, con independencia del Blog al que estén asociados. Es decir, una accion como /posts/index.

Por supuesto, cada Blog tiene su página principal, que muestra una lista de posts. Mi primera tendencia fue a crear una accion /blogs/view/blog_id, pero me dije: "Un momento, esto no es más que un /posts/index/blog_id. Puedo hacer que /posts/index maneje la situación de listar sólo los post de un blog determinado".

Así que en principio creé una acción que modificaba la búsqueda de datos en función de si se pasaba un parámetro para indicar un blog. Luego en la vista, incluí una lógica para actuar de manera ligeramente distinta si se listaban los post de todo el sitio o si eran sólo los del blog indicado. Como tampoco me gustaba, preparé dos vistas y dejé que el controller eligiese la adecuada.

Sin embargo, esta solución tampoco me convence. Finalmente he decidido crear dos acciones en el PostsController (index y blog), cada una con su vista. El Controller ahora no toma tantas decisiones, y la vista tampoco. Pero ¿Qué opinas tú?

lunes, 25 de agosto de 2008

Aprendiendo de un refactoring

He comenzado a reescribir algún código procedente de un proyecto anterior para el nuevo que estoy emprendiendo. En concreto un helper para genera tablas de administración de registros más flexibles y configurables que las generadas por Bake.

Cuando tu propio código ha envejecido unos meses puede llegar a hacerse bastante complicado de leer. De hecho, para ser sinceros, no había por dónde cogerlo. En otras palabras, no sabía por dónde empezar. Recordaba vagamente no estar muy satisfecho con la forma en que había escrito ese helper ya que, aunque funcionaba bien, no me atrevía normalmente a tocarlo mucho por si acaso.

Entonces se me ocurrió que ya que no veía como mejorar aquello, por lo menos intentaría hacerlo más legible. En cuanto empecé a conseguir legibilidad me fue fácil darme cuenta de dónde estaba metiendo la pata (en casi todo, pero esa es otra historia).

Volver pronto a casa

Por ejemplo, tengo (tenía) mucha tendencia a escribir código así:

if ($hay_que_procesar_esto == true) {
procesar...
procesar...
procesar
} else {
return false;
}
return $resultado_del_proceso;


Lo cual en su momento me parecía de lo más elegante y tal y cual. Sin embargo, tras leer este artículo de Felix Geisendörfer, caí en la cuenta de que la solución "Vuelve pronto a casa" es bastante más legible;

if (!$hay_que_procesar_esto) {
return false;
}

procesar...
procesar...
procesar...

return $resultado_del_proceso;


La lógica que apoya esta forma de organizar el código es muy convincente. Si escribes una función o un método, primero intenta lidiar con las excepciones y regresa. Deja para el final el algoritmo general, que podrá ejecutarse sin problemas y será más fácil de depurar llegado el caso.

Normaliza los datos

En mi caso, el helper podría lidiar con un array de registros resultado de una búsqueda con un model::find(). Pero ¿qué ocurre si quiero usar registros relacionados? La estructura del array es ligeramente distinta ¿no? Y solo requiere un sencillo bucle pasar de una a otra.

Pues nada, lo lógico (yo no lo hacía en el método que estaba reescribiendo) es chequear si los datos se ajustan a una estructura dada y, si no es así, normalizarlos antes de empezar.

De otro modo, lo que consigues es tener que crear un algoritmo o un proceso distinto para cada tipo de estructura posible y, si bien puede haber casos en que sea necesario hacerlo así, lo más seguro es que la modificación sea fácil.

No te repitas

Una de las cosas que más me llamó la atención fue comprobar que mi código hacía un montón de tareas duplicadas. A veces con los mismos datos de forma sucesiva (lo que supone el doble de tiempo de ejecución). Eso no era evidente en la primera versión, pero al reescribir esas situaciones empezaron a manifestarse con claridad.

La repetición de código no se refiere solo a ejemplos tan burdos como el mío, sino incluso al problema más sutil de los métodos que son muy similares entre sí y que tal vez sólo se diferencian en el ámbito de los datos que recuperan. Por ejemplo, un método index en un controlador que devuelve todos los registros existentes y otro método populares que devuelve todos los registros que tienen un mayor número de peticiones. La única diferencia entre ambos métodos sería una condición de búsqueda.

Como seguramente sabrás, el principio DRY (Don't Repeat Yourself) aboga por un código en el que se eviten al máximo las repeticiones. En el artículo de Nate Abele Art, MVC and CakePHP puedes encontrar un estudio muy interesante en el que se explica la aplicación de este principio. Es un clásico.

jueves, 7 de junio de 2007

Avances sorprendentes

He tenido un día bastante bueno con Cake. Los avances empiezan a ser significativos y algunas cosas van estando más claras.

Los cambios del behavior (antes helper, antes behavior) joinTable me han permitido desarrollar un poco más los conceptos MVC y los fat models. Voy descargando al controlador de trabajo y se lo paso al modelo. Es muy interesante, por ejemplo, cómo se programa la duplicación de registros, o la creacion de un modelo desde otro.

Por otro lado, mis vistas piden muchos helpers. :-)

El helper simpleTable (que aún no he publicado) me ha aportado mucho en trabajar con arrays para pasar parámetros complejos y también para conocer los entresijos del HtmlHelper.

Incluso he trabajado con la paginación y ya tengo en mente preparar un helper para generar navegadores de listados. Eso me ha llevado a utilizar la sesión para almacenar datos. Tengo que ver algunas cosas más sobre eso, porque hay un par de aspectos del paginator helper que me gustaría dominar mejor.

Por otro lado, he empezado a salir del nicho "una sola tabla cada vez" y me voy moviendo en el terreno de las asociaciones. Por ejemplo, en el modelo Recurso, he incluido un callBack beforeSave con el que guardar Categorías nuevas cuando al usuario no le llegan las que le ofrece el menú desplegable.

Otro aspecto que he comenzado a mirar es el de la maquetación. Creo que empiezo a entender como funciona el tema de los layouts y su relación con las vistas y, sobre todo, los elements. He estado jugando con las css y un layout para ver qué tal se presenta Cake. Y la verdad es que la pinta es buena. Una de las grandes ventajas es que se puede diseñar la presentación por un lado e integrar la aplicación en el diseño, porque los layouts no son otra cosa que páginas (X)HTML.

¡Hasta me he pasado a los PNG para iconcitos y todo!

Sobre esto de la presentación hay un tema que no tengo del todo claro, y es cómo trabajar cuando quiero que una acción no vaya a la vista principal. Por ejemplo, si un Element tiene un enlace que hace que se actualice. ¿Ajax? ¡Demasiado pronto para mí, creo! Pero ¡quién sabe!.

Quedan aún muchas cosas que aprende, eso sí. Sin embargo, parece que he pasado el punto de "no retorno".

Algo que me he dado cuenta es que CakePHP es un framework que deja mucho campo al PHP. Me explico. Muchos frameworks son como lenguajes en sí mismos, con centenares de funciones que hacen de todo. Sin embargo, Cake proporciona más bien una base de trabajo y llega hasta el límite donde no quieres que hagan las cosas por ti. Creo que es bueno que sus desarrolladores no quieran extender demasiado el API.

Es decir. Te descarga del tema del movimiento de datos, creación de formularios, gestión de "infraestructura" y todas esas cosas de rutina que hay que hacer. Sin embargo, no intenta tomar decisiones por el programador. No sé si me explico del todo.

Se nota más en las vistas. Aunque es cierto que el FormHelper hace muchísimo, la verdad es que las responsabilidad de la vista te la deja a ti, no hay funciones o helpers para cosas muy específicas. Los que hay tienen bastante fundamento y los resultados son muy buenos. Pero a la hora de presentar datos, por ejemplo, te deja las manos libres. Al principio puede parecer un poco jorobado, pero ahí entra tu habilidad para decidir cuando necesitas un helper y escribirlo.

lunes, 4 de junio de 2007

Progresos


Poco a poco voy consiguiendo progresos en el aprendizaje de CakePHP. Hace más o menos una semana que comencé a intentar desarrollar algo concreto y lo cierto es que va funcionando. No está terminado, pero me ha servido para aprender unas cuantas cosas.

La imagen de arriba muestra un típico "index" de una tabla del modelo paginada. Lo más complejo ha sido crear la vista y tengo que probar algunas estrategias para ver qué hacer cuando no hay datos que mostrar. Sin embargo, ahí tengo botones de acciones personalizados, etc. A ver si encuentro iconos mejores, por cierto. ¡Ah! Y alguna manera de dar un mejor aspecto al paginador.

Como se puede ver, hay imágenes en los registros. Esto me ha servido para aprender a incluir Behaviors. En concreto uno "bastante bueno" para subir archivos, con algunas características curiosas, como la capacidad de autoenrutar el archivo en función de su tipo mime. También funciones para validar por tipo o subtipo mime y otras cosillas. Tengo todavía un problema con la sobreescritura de archivos, que ahí peta. También quiero añadirle la posibilidad de guardar los archivos (o referencias a ellos) en una tabla común.

También he aprendido a crear elements y algo sobre usar requestAction. Esto es útil para acceder a resultados de (otras) acciones de (otros) controladores, aunque hay quien sugiere algunos enfoques menos cargantes para la base de datos. Supongo que eso es matizar mucho para alguien que apenas lleva una semana escribiendo sobre CakePHP.

Entre otros próximos objetivos tengo la posibilidad de crear algún Helper para las tablas de datos, al menos para los tipos más comunes de acciones, seleccionar columnas que quiero mostrar/no mostrar/ocultar, etc. Aún me quedan un montón de historias para aprender, pero lo bueno es que lo que va saliendo... pues va saliendo. El scaffolding de Cake es como una red de seguridad.

Otro tema, es que tengo que ver cómo añadir en la acción delete algo que se encargue de borrar las imágenes asociadas a un recurso. Supongo que en BeforeDelete del modelo o algo así. Tengo que verlo.