Cuando se habla del patrón Modelo-Vista-Controlador a veces no queda claro cuál es el papel del Controlador.
Veamos:
El Modelo lleva la lógica de negocio, que básicamente es la representación de las entidades que la aplicación ha de manejar y sus relaciones. Así en una aplicación de gestión de bibliotecas tendremos modelos para los libros, socios, préstamos, etc., así como métodos para prestar libros, devolverlos, y otros muchos.
La Vista lleva la lógica de presentación y cada vista sabe mostrar unos datos que recibe y nada más.
¿Y el controlador? Solemos decir que el controlador pide datos al modelo y se los pasa a la vista, pero eso creo que no deja suficientemente claro su papel.
El controlador se encarga de la lógica de la aplicación. Esta lógica es la que representa lo que ha de hacer el sistema para solicitar los datos adecuados al modelo y qué hacer con ellos. Eso incluiría también saber si hay un usuario autorizado en la sesión, si hay variables del entorno que se deban tener en cuenta (como límites de paginación, variables de sesión y otros), saber qué hacer si no se obtienen datos del modelo, etc.
Diario de aprendizaje del framework CakePHP. Otras notas de desarrollo y diseño web, realizado sobre Mac.
Mostrando entradas con la etiqueta MVC. Mostrar todas las entradas
Mostrando entradas con la etiqueta MVC. Mostrar todas las entradas
lunes, 1 de febrero de 2010
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ú?
Etiquetas:
estilo de programación,
generalidades,
MVC
miércoles, 26 de marzo de 2008
Personalizando Find y adelgazando el Controller
Aprendí esta técnica gracias a un post de Cakebaker que, a su vez, se basa en uno de Nate Abele más general sobre buenas prácticas en programación MVC con CakePHP.
La idea, una vez conocida, parece obvia. Desde la última beta de CakePHP se ha cambiado la sintaxis del método Model->find, de modo que ahora se llama con dos parámetros, uno indicando el tipo de búsqueda y otro con un array de opciones, que incluye, entre otros, las condiciones de búsqueda, límites, ordenación, etc.
Esta sintaxis a mi me ha resultado muy práctica, ya que no tienes que andar pendiente de si pasas los parámetros en el orden adecuado, etc.
Pero la técnica consiste en ir un poco más allá y reescribir el método a fin de disponer de más tipos de búsquedas personalizadas para nuestras necesidades en cada modelo. La reescritura sería más o menos así:
Fíjate que para seguir utilizando los tipos predefinidos por Cake no tienes más que llamar a parent::find con los parámetros adecuados.
Hasta ahora podías (y se sigue pudiendo, claro) escribir métodos del modelo especializados en buscar resultados encapsulando condiciones. Por ejemplo, en un modelo Post, podríamos tener métodos como recientes(), valorados(), destacados(). (Espero que te hagas una idea de cuál sería su funcionamiento).
Con la nueva sintaxis los podríamos reescribir así: find('recientes'), find('valorados'), find('destacados').
Superficialmente esta reescritura no ofrece ninguna ventaja sobre esta forma de trabajar, pero si lo piensas un poco te darás cuenta que puede ser muy útil en ciertas situaciones.
En particular, cuando los diversos tipos de búsqueda comparten ciertas condiciones por defecto. En el ejemplo, podría ser que tu modelo Post sólo deba devolver registros que tengan el campo "publicar" en 1, y las fechas de "publicación" y "caducidad" adecuadas. Evitarías tener que repetir esas condiciones comunes en cada método, centrándote sólo en las específicas .
Además, puedes usar el parámetro $options para pasar opciones que no están contempladas por CakePHP. Por ejemplo, yo estoy usando esta técnica para pasar a find, un rango de fechas con las que seleccionar eventos de un calendario. Pero también podría ser el usuario actual para seleccionar Posts a los que tiene derecho a acceder y un largo etcétera de posibilidades.
Acabo de utilizar esta técnica, como he comentado, con una aplicación de Agenda o Calendario, en la que he movido toda la lógica de búsqueda de eventos al modelo. He añadido hasta siete nuevos tipos de búsqueda (días sueltos, esta semana, hoy, rango de fechas...). Ahora el modelo es un auténtico "fat model", mientras que las acciones del controlador han adelgazado enormemente y son mucho más comprensibles.
En la mayor parte de los casos, los nuevos tipos que he definido lo único que hacen es preparar condiciones y opciones específicas para llamar finalmente a parent::find('all', $options). Es decir, no es que haya que reescribir el método find por completo o construir querys personalizadas, pero podemos simplificar muchísimo la llamada desde el Controller, incluso definiendo nuevas opciones más significativas.
Por ejemplo, en mi modelo Evento he definido un tipo de búsqueda que he denominado 'week', y que sirve para obtener los eventos activos desde hoy hasta dentro de 7 días. En el método personalizado lo único que hago es determinar la fecha de hoy y la fecha de 7 más adelante:
Luego, inserto esos valores en el array de condiciones que voy a enviar al método parent::find('all')
En este caso, te hago notar que no hago la búsqueda en el "case", sólo establezco los valores límite de las fechas, pero podría perfectamente hacer una llamada a parent::find si ya tengo todo lo que necesito.
Otros tipos de búsqueda hacen algo similar, partiendo de algunas opciones que paso (o que no paso, por lo que toman ciertos valores por defecto), genero las fechas límite en las que busco los eventos.
Espero haberme explicado. De todos modos, lo mejor es leer los artículos originales que enlazo al principio.
Ahora estaría bien si Controller-->paginate() llega a soportar esta sintaxis. De momento es un punto un poco oscuro para mí, coordinar paginación y búsquedas.
La idea, una vez conocida, parece obvia. Desde la última beta de CakePHP se ha cambiado la sintaxis del método Model->find, de modo que ahora se llama con dos parámetros, uno indicando el tipo de búsqueda y otro con un array de opciones, que incluye, entre otros, las condiciones de búsqueda, límites, ordenación, etc.
Esta sintaxis a mi me ha resultado muy práctica, ya que no tienes que andar pendiente de si pasas los parámetros en el orden adecuado, etc.
Pero la técnica consiste en ir un poco más allá y reescribir el método a fin de disponer de más tipos de búsquedas personalizadas para nuestras necesidades en cada modelo. La reescritura sería más o menos así:
function find ($type, $options = array()) {
switch ($type) {
case 'tipoPersonalizado':
// Código
break;
default:
return parent::find($type, $options);
break;
}
}
Fíjate que para seguir utilizando los tipos predefinidos por Cake no tienes más que llamar a parent::find con los parámetros adecuados.
Hasta ahora podías (y se sigue pudiendo, claro) escribir métodos del modelo especializados en buscar resultados encapsulando condiciones. Por ejemplo, en un modelo Post, podríamos tener métodos como recientes(), valorados(), destacados(). (Espero que te hagas una idea de cuál sería su funcionamiento).
Con la nueva sintaxis los podríamos reescribir así: find('recientes'), find('valorados'), find('destacados').
Superficialmente esta reescritura no ofrece ninguna ventaja sobre esta forma de trabajar, pero si lo piensas un poco te darás cuenta que puede ser muy útil en ciertas situaciones.
En particular, cuando los diversos tipos de búsqueda comparten ciertas condiciones por defecto. En el ejemplo, podría ser que tu modelo Post sólo deba devolver registros que tengan el campo "publicar" en 1, y las fechas de "publicación" y "caducidad" adecuadas. Evitarías tener que repetir esas condiciones comunes en cada método, centrándote sólo en las específicas .
Además, puedes usar el parámetro $options para pasar opciones que no están contempladas por CakePHP. Por ejemplo, yo estoy usando esta técnica para pasar a find, un rango de fechas con las que seleccionar eventos de un calendario. Pero también podría ser el usuario actual para seleccionar Posts a los que tiene derecho a acceder y un largo etcétera de posibilidades.
Acabo de utilizar esta técnica, como he comentado, con una aplicación de Agenda o Calendario, en la que he movido toda la lógica de búsqueda de eventos al modelo. He añadido hasta siete nuevos tipos de búsqueda (días sueltos, esta semana, hoy, rango de fechas...). Ahora el modelo es un auténtico "fat model", mientras que las acciones del controlador han adelgazado enormemente y son mucho más comprensibles.
En la mayor parte de los casos, los nuevos tipos que he definido lo único que hacen es preparar condiciones y opciones específicas para llamar finalmente a parent::find('all', $options). Es decir, no es que haya que reescribir el método find por completo o construir querys personalizadas, pero podemos simplificar muchísimo la llamada desde el Controller, incluso definiendo nuevas opciones más significativas.
Por ejemplo, en mi modelo Evento he definido un tipo de búsqueda que he denominado 'week', y que sirve para obtener los eventos activos desde hoy hasta dentro de 7 días. En el método personalizado lo único que hago es determinar la fecha de hoy y la fecha de 7 más adelante:
case 'week':
$options['to'] = date('Y-m-d', strtotime( "+ 7 Day"));
$options['from'] = date('Y-m-d');
break;
Luego, inserto esos valores en el array de condiciones que voy a enviar al método parent::find('all')
$conditions = array(
'Evento.fecha_inicio' => '<= '.$options['to'],
'or' => array(
'Evento.fecha_fin' => '>= '.$options['from'],
'and' => array(
'Evento.fecha_fin' => null,
'Evento.fecha_inicio' => '>= '.$options['from']
)
),
'Evento.publicar' => 1
);
if (!isset($options['conditions'])) {
$options['conditions'] = $conditions;
} else {
$options['conditions'] = array_merge($options['conditions'], $conditions);
}
return parent::find('all', $options);
En este caso, te hago notar que no hago la búsqueda en el "case", sólo establezco los valores límite de las fechas, pero podría perfectamente hacer una llamada a parent::find si ya tengo todo lo que necesito.
Otros tipos de búsqueda hacen algo similar, partiendo de algunas opciones que paso (o que no paso, por lo que toman ciertos valores por defecto), genero las fechas límite en las que busco los eventos.
Espero haberme explicado. De todos modos, lo mejor es leer los artículos originales que enlazo al principio.
Ahora estaría bien si Controller-->paginate() llega a soportar esta sintaxis. De momento es un punto un poco oscuro para mí, coordinar paginación y búsquedas.
Suscribirse a:
Entradas (Atom)