CakePHP – Handler afterFind()

Introducción Una de las herramientas que provee el Framework CakePHP para inflar los modelos con responsabilidad son los handlers, estas son funciones que se ejecutan cuando se dispara un evento. Pero no todo es color de rosa, y la mala utilización de los mismos puede generar más de un inconveniente, sumado a esto la documentación […]

Julián Butti | Syloper Julián Butti

Publicado el 13/06/2014

Introducción

Una de las herramientas que provee el Framework CakePHP para inflar los modelos con responsabilidad son los handlers, estas son funciones que se ejecutan cuando se dispara un evento. Pero no todo es color de rosa, y la mala utilización de los mismos puede generar más de un inconveniente, sumado a esto la documentación sobre algunos de estos handlers no es del toda clara y completa.

Es por esto que vamos a explicar lo más detalladamente posible una forma posible de manejar los datos en la función afterFind() de los modelos de manera que no tengamos resultados inesperados.

La definición de la función

Revisando la documentación oficial de CakePHP (http://book.cakephp.org/2.0/en/models/callback-methods.html#afterfind) podemos ver que esta función recibe 2 argumentos:
– El primero $results que es un array con el cual vamos a trabajar para modificar los datos antes de pasar los mismos al controlador.

– El segundo argumento es $primary que remitiendonos nuevamente a la documentación «indica si el modelo originó la consulta o no». En principio podemos preguntarnos para qué queremos tener esta información en el handler pero luego de habernos chocado contra algunos resultados inesperados entendemos el por qué de este argumento. Este argumento nos va a decir de alguna manera la forma que va a tener el array $results para poder trabajarlo de forma correcta.

Cuando se dispara la función

Esta función se llama cuando se llama al modelo en cuestión para recuperar datos de la base de datos. En principio podemos suponer que eso se sucede cuando hacemos una llamada de la forma $this->Model->find() desde el controlador o en su defecto $this->find() desde el modelo, pero esta no es la única situación en la cual se va a ejecutar nuestra función y acá comienza nuestro (por ahora) problema.

Una función afterFind() se llama también cuando:

– Hacemos un $this->Model->read(); desde el controlador, o en su defecto $this->read() desde el modelo.

– Cuando se le pide información al modelo por haber hecho un find o un read de un modelo relacionado (si seteamos la recursividad adecuada). Por ejemplo si el modelo Post tiene una asociación $hasMany con el modelo Comment.

<?php

class PostsController extends AppController {

    function index() {

        $this->Post->recursive = 2;

        $posts = $this->Post->find('all',array());

    }

}

?>

Es natural pensar que además de la función afterFind() del modelo Post también se va a ejecutar la función afterFind() del modelo Comment.

El problema

El principal problema que se nos presenta es el saber con qué forma va a llegar el argumento $results y esta es una cosa que a mi parecer le falta una vuelta de rosca al cakePHP. La forma de $results va a depende de el origen de la llamada del afterFind().

Esto puede variar entre estas variaciones:

– Cuando $primary es true:

 

array(
    '0' => array(
        'Model' => array(
            'id' => 1
        )
    )
)

 // Cuando $primary es false nos chocamos con un formato de $results para cada asociación

hasOne


array(
    'id' => 1
)

HABTM

 
array(
    '0' => array(
        'id' => 1
    )
)

hasOne, hasMany, belongsTo

array(
    '0' => array(
        'Model' => array(
            'id' => 1
        )
    )
)

HABTM, hasMany


array(
    '0' => array(
        'Model' => array(
            '0' => array(
                'id' => 1
            )
        )
    )
)

Esto produciría una cantidad de ifs inmanejables dentro del afterFind() del modelo.

La solución

Crear un set de funciones: afterFind() llama a _afterFind() alojadas ambas en AppModel y que esta función chequee el formato de $results y llame a la función doAfterFind() definida en el Modelo y envíando al mismo los datos con un mismo formato definido:

 

<?php

class AppModel extends Model {

    public function afterFind($data, $primary = false) {
        return $this->_afterFind($data, $primary);
    }
   
    function _afterFind($data, $primary) {
        if ( $primary ) {
            foreach ( $data as $key => $val ) {
                if ( isset($val[$this->alias]) ) {
                    $data[$key][$this->alias] = $this->doAfterFind( $data[$key][$this->alias] );
                }
            }
   
        } else {
   
            if ( isset($data[$this->primaryKey]) ) {
                $data = $this->doAfterFind( $data );
            } else {
               
                foreach ( $data as $key => $val ) {
                   
                    if ( isset($val[$this->alias]) ) {
                        if ( isset($val[$this->alias][$this->primaryKey]) ) {
                            $data[$key][$this->alias] = $this->doAfterFind( $data[$key][$this->alias] );
                        } else {
                            foreach ( $data[$key][$this->alias] as $key2=> $val2 ) {
                                $data[$key][$this->alias][$key2] = $this->doAfterFind( $data[$key][$this->alias][$key2] );
                            }
                        }
                    }
                }
            }
   
        }

        return $data;
    }
 
}

?>


<?php
class Servicio extends AppModel {


    function doAfterFind($data) {
       
        if (isset($data['created']) && !empty($data['created'])) {
            $created = date_create($data['created']);
            $data['created'] = date_format($created, 'd-m-Y H:i');
        }
       
        if (isset($data['modified']) && !empty($data['modified'])) {
            $modified = date_create($data['modified']);
            $data['modified'] = date_format($modified, 'd-m-Y H:i');
        }
       
        if (isset($data['fecha_inicio']) && !empty($data['fecha_inicio'])) {
            $fecha_inicio = date_create($data['fecha_inicio']);
            $data['fecha_inicio'] = date_format($fecha_inicio, 'd-m-Y H:i');
        }
       
        return $data;
    }
}
?>

 

CRM inmobiliario: más clientes, propiedades y ventas

¿Tenés una inmobiliaria y querés difundir tus propiedades? ¿Necesitás un sitio web que atraiga nuevos clientes y potencie tu negocio? Sin dudas, un CRM inmobiliario...


Compromiso por un internet saludable

Ver publicación ->

Producto mínimo viable: qué es y cómo se desarrolla

En el mundo del desarrollo de software, y de los negocios en general, es muy común que surjan nuevas ideas permanentemente. Pero algunas funcionan y...


Envianos tu consulta





También podés escribirnos a hola@syloper.com