Mostrando entradas con la etiqueta cakePHP. Mostrar todas las entradas
Mostrando entradas con la etiqueta cakePHP. 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!"

jueves, 17 de septiembre de 2009

DRY, pero no tan DRY

A la metodología de programación que busca no repetir innecesariamente código se la conoce como DRY (Don't Repeaty Yourself, No Te Repitas).


Hace unos posts escribía sobre una técnica que consistía en escribir algunas acciones básicas en el AppController, a modo de scaffolding, de modo que no necesitase escribir otra vez ese código en los nuevos controladores que iba creando.

A este post me respondía AD7six, del que había tomado la idea en un post suyo antiguo. En su comentario me hacía ver que, a la larga, me iba a encontrar con problemas a medida que los controladores empezasen a tener excepciones y que acabaría reescribiendo todo.

Por supuesto, AD7six tiene toda la razón al indicar esto, aunque yo creo que la técnica puede seguir siendo válida para  partes de las aplicaciones que sólo necesiten acciones CRUD muy básicas (como mantenimiento de listas de opciones, categorías, etc). En realidad, también se podría usar el scaffolding.

¿Qué solución tenemos a esta situación en la que el código es repetitivo y sin embargo no debería dejarse en un método genérico?

Pues la respuesta es Bake, la utilidad de líneas de comandos para generar Modelos, Controladores y Vistas en CakePHP.

¿Y si no te gusta el código que genera? Pues a aprender a crear tus propias task para montar controladores, y modelos. Y plantillas para las vistas.

De este modo, podemos combinar lo mejor de ambos mundos: no tener que escribir 20 veces el mismo código (lo hace bake) y poder adaptarlo según sea necesario.

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.

domingo, 16 de agosto de 2009

lunes, 3 de septiembre de 2007

Cake Bake y MAMP

Llevaba un tiempo bastante molesto para mi incapacidad para usar las herramientas de generación de CakePHP, la antigua utilidad Bake (actualmente cake bake). El problema es que siempre me salía con un error de conexión con la base de datos.

Yo utilizo en mi máquina de trabajo el paquete MAMP (Apache-MySQL-PHP para Mac OS X), instalado tal como viene por defecto.

El caso es que he podido entender el problema y solucionarlo.

bake busca comunicarse con la base de datos a través del socket

/var/mysql/mysql.sock

Pero si instalamos MAMP sin tocar su configuración para nada, el socket está en:

/Applications/MAMP/tmp/mysql/mysql.sock

La solución es crear un enlace simbólico:

ln -s /Applications/MAMP/tmp/mysql/mysql.sock /var/mysql/mysql.sock

Pero hay un par de requisitos previos. Primero tenemos que crear un directorio mysql bajo var:

cd /var
sudo mkdir mysql

y luego crear el enlace

sudo ln -s /Applications/MAMP/tmp/mysql/mysql.sock /var/mysql/mysql.sock

Y listo.

Nota final

Una cosilla... uso sudo porque al menos en Mac OS X mi usuario de trabajo no tiene permisos sobre la carpeta var para crear el directorio y el enlace simbólico, sin embargo, éste funciona sin ningún problema.

martes, 21 de agosto de 2007

CakePHP-es

Se acaba de poner en marcha hace unos días CakePHP-es , un espacio comunitario sobre CakePHP en español. La iniciativa se ha iniciado con una wiki-traducción del manual actual de CakePHP, lo que va a suponer un alivio para buena parte de los que se están iniciando en este framework.

Esta traducción no es oficial, pero está autorizada por la CakePHP Software Foundation.

Esta web se une al grupo de google ya existente y bastante activo.

Además, se ha puesto en marcha un pastebin, que es una herramienta en línea para publicar y revisar código colectivamente.

O sea, que ya cuentas con unos cuantos recursos en español acerca de CakePHP. ¡A cocinar!

viernes, 15 de junio de 2007

Anatomía de una acción

Uno de los conceptos básicos que hay que entender cuando se empieza con CakePHP es el de cómo "funcionan" las acciones en los controladores. Como novato que soy tropiezo con esta piedra varias veces al día, por lo que me siento muy autorizado a exponer algunas ideas sobre el particular.

Cuando pedimos una URL de una aplicación CakePHP, en el clásico formato /controlador/acción, es el controlador el que la recibe, prepara lo necesario y ejecuta la acción.

La fase de preparación consiste en "averiguar" lo que ha pasado ahí fuera, en el mundo de los usuarios, y poner esa información a disposición de la acción de una manera estructurada.

Básicamente, lo que el controlador mira son dos cosas:
  • Los datos que hayan sido recogidos en un formulario y enviados mediante POST (normalmente). Los encontrarás en Controller::data
  • Los argumentos que hayan sido pasados por la URL. Los encontrarás en el array Controllers::passedArgs
Aparte de estos datos básicos, puedes encontrar mucha información importante en Controller::params, o detalles sobre la petición si usas RequestHandler. Pero para lo básico vamos a quedarnos sólo con los argumentos y los datos de formulario.

Datos de formulario

Si el usuario ha rellenado un formulario y apretado el botón Submit, Controller::data será poblado por los datos de Post. Nuestra acción tendrá entonces que detectar esta situación y abrir dos "modos" de trabajo según haya datos para procesar el formulario o no.

Habitualmente, si no hay datos la acción tendrá que mostrar el formulario adecuado.

Lo anterior establece algo así como dos "modos" de la acción: uno de postproceso de formulario y otro de preparación del mismo.

Argumentos pasados

Dentro de cada uno de los modos anteriores, los argumentos pasados por URL nos sirven para modular el comportamiento concreto de la acción. Por ejemplo, en el típico caso de una acción edit, normalmente pasamos un argumento con el id del registro que queremos editar. Por lo tanto, nuestra lógica será:
  • Si tenemos datos del formulario se trata de una actualización y hay que tratar de guardar los datos nuevos en el registro con el id indicado.
  • Si no tenemos datos del formulario, se trata de cargar el registro cuyo id se nos ha pasado y poner un formulario ya cubierto con esos datos para que el usuario los modifique.
CakePHP es lo bastante inteligente como para no obligarnos a leer el array Controller::passedArgs cuando ciertos argumentos sean específicos para la acción. Es el típico caso del id en acciones tipo edit o view.

Lo que hace es mapear estos argumentos de la URL en los argumentos que hayamos definido para nuestra acción. En cierto modo, esto nos define la estructura de la URL. Por ejemplo:

function accion ($id, $title= false)

espera una URL de la forma:

/controlador/accion/12/ejemplo
/controlador/accion/15

El argumento id sería obligatorio y title opcional.

Los argumentos que no sean específicos podemos leerlos en Controller::passedArgs, que es un alias a Controller::params[pass] (y a Controller::passed_Args, por cierto).

Una url con argumentos tiene la forma

/controller/accion/12/pagina:5/ordenar:id/

En este caso 12, seria pasado como argumento a la acción "accion", mientras que encontraríamos pagina y ordenar en el array Controller::passedArgs.

Por supuesto, hay muchas variedades de acciones. No todas ellas tienen que lidiar con formularios, algunas solo van a presentar un contenido estático, etc. Pero básicamente todas siguen un mismo esquema:
  • Compobar si hay información procedente de ciertas fuentes (argumentos, formulario)
  • Si hay información, ver qué tengo que hacer con ella y presentarla al usuario
  • Si no hay información, presentar una vista que la solicite al usuario

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.