¡Cuanto tiempo sin postear nada!

Hoy vengo con la solución a un pequeño problema que suele ocurrir cuando se desarrolla con CakePHP y la base de datos te viene "heredada". Todos sabemos lo doloroso que es cambiar el nombre de un campo de una tabla que ya se encuentra en producción si no hemos encapsulado correctamente el acceso a dichos datos. Y como aún no conozco ninguna aplicación donde todas las buenas prácticas conocidas por el desarrollador se apliquen, sé que es más que probable que en tu aplicación tengas una tabla donde a alguien se le hubiera ocurrido la magnífica idea de bautizar a una columna con uno de los tres nombres reservados que CakePHP utiliza para las marcas de tiempo en base de datos.

Empezando por el principio, el ORM de CakePHP es muy mejorable. En un intento de mejorar la experiencia del desarrollador, el equipo de CakePHP ha hecho que su ORM busque por tres columnas nombradas de manera estándar para que mágicamente en ellas se pongan campos sin que el programador lo pida. Esto puede ser muy bueno si todas tus aplicaciones son CakePHP, y si tus desarrollos están orientados a hacer uso de este framework. ¿Qué pasa si no? Pues que CakePHP va a tocar columnas sin que tu lo hayas pedido (manda huevos) y encima, no te va a ofrecer una alternativa "sencilla".

Estas serían los columnas "malditas":

  • created: Cuando se hace una inserción (INSERT), pone la fecha actual (no utiliza NOW() ni su equivalente en otras bases de datos, sino la función date de PHP, ...).
  • modified: Cuando se realiza una actualización (UPDATE), pone la fecha actual sin tocar created.
  • updated: Se trata de un alias de modified

¿Qué hacemos entonces si no queremos cambiar dicho valor? Pues tenemos que definir un callback beforeSave. Si alguien ha encontrado una mejor manera, sería genial que lo compartiera en comentarios. Aunque pegándole un ojo al código de la clase Model.php del framework, no he podido ver que se pueda hacer de otra forma.

<?php
App::uses('AppModel', 'Model');
class Product extends AppModel {
    public function beforeSave($options = array()) {
        parent::beforeSave($options);
        unset($this->data[$this->name]['updated']);
        unset($this->data[$this->name]['created']);
        unset($this->data[$this->name]['modified']);
        return true;
    }
}

En el ejemplo, se cargaría los tres campos. Hay que notar que para que el código sea E_STRICT, hay que mantener el perfil del método heredado. Y como no, propagar los eventos hacia arriba, ya que si Product heredara de otro modelo que no fuera AppModel debería mantener el comportamiento de los beforeSave definidos en la clase padre.

Otro detalle "Cakero" que se suele escapar, es el de la llamada a App::uses que parece innecesaria. No lo es. Da la casualidad que si todos tus modelos heredan de AppModel no hace falta (el framework la incluye por tí) pero si es otro modelo porque utilizas algún tipo de clase común para los modelos con funcionalidad compartida, no funcionará mágicamente.

Bueno, espero que el post os sea de ayuda para lidiar con CakePHP y su ORM.

Saludos y salud!