Este tipo de asociaciones require una tabla íntermedia de unión (join table) que siguiendo las convenciones de CakePHP nombraríamos en este caso posts_tags. Ya sabes.
Post habtm Tag a través de posts_tags
Cuando hacemos un búsqueda en Post, CakePHP automágicamente incluye en los resultados las Tag asociados a cada Post. Pero, ¿podemos buscar los Post que estén asociados a una o varias Tag además de otras condiciones?
La respuesta es que directamente no podemos hacerlo ya que en las relaciones habtm no se hace el "join" automático de las tablas (al contrario de lo que ocurre en las relaciones hasMany o belongsTo).
Crear una query en SQL para utilizar con Model::query() es una solución, pero tiene el inconveniente de que perdemos algunas de las facilidades propias de CakePHP (básicamente los callbacks como afterFind o beforeFind) y otras ventajas.
En CakePHP 1.2 es posible hacer esto de una manera bastante sencilla aunque require "modelizar" la tabla intermedia. Primero, tenemos que crear un modelo para la tabla posts_tags y especificar sus relaciones con las tablas Post y Tag. Siguiendo las convenciones de CakePHP nos queda así:
<?php
class PostsTag extends AppModel {
var $belongsTo = array(
'Tag',
'Post');
}
?>
En el modelo Post tenemos que especificar que la relación con Tag se va a hacer "con" (with) ese modelo intermedio.
var $hasAndBelongsToMany = array (
'Tag' => array(
'className' => 'Tag',
'with' => 'PostsTag'
)
);
Hasta aquí el trabajo preparatorio. Ahora veamos un ejemplo de su uso. En el model Post tengo un método "publicados" que me devuelve los Post cuya fecha de publicación es anterior a la actual, cuyo flag de publicar está a 1 y que están etiquetados con una tag que le pasamos al método.
function publicados ($tag = false) {
$now = date ('Y-m-d');
$conditions = array (
'Tag.tag' => $tag,
'Post.publicar' => '1',
'Post.publicacion' => "<= $now"
);
return $this->PostsTag->find('all', array('conditions' => $conditions));
}
Espero que se entienda. Las condiciones no deberían tener mucha dificultad. Toda la clave es el find que hacemos sobre el modelo intermedio (PostsTag) asociado a Post, no directamente a Post. (Nota: he suprimido la parte del código que gestiona lo que hay que hacer si no se pasa ninguna $tag).
Algunas referencias que me sirvieron de punto de partida:
Modelizing HABTM join tables in CakePHP 1.2: with and auto-with models de Mariano Iglesias
Modeling relationships in CakePHP (faking Rails’ ThroughAssociation) de Felix Geisendörfer
3 comentarios:
Sólo una cosa. Gracias por este artículo, era justo lo que necesitaba.
Saludos,
PD: Por cierto, te sobra un punto y coma en el último bloque de código: 'Tag.tag' => $tag; <--- esto debe ser una coma ;-)
De nada.
Ya he corregido el "typo".
Yo tengo una tabla llamada producto que tiene una relación HABTM con otras 2 tablas: secciones y categorías, como puedo recuperar los productos que están en una sección determinada y a su vez en una categoria determinada sin hacer un query personalizado.
Publicar un comentario