jueves, 28 de agosto de 2008

Paginación con relaciones HABTM

Bueno, el artículo de Cakebaker que puedes leer pinchando en el título o aquí, me ha servido para resolver un problema que me lleva ocupando todo el día. En fin, Daniel Hofstetter es un capo en este de CakePHP.

La cosa es más o menos así:

Tengo un model Blog y un model User, de forma que Blog hasAndBelongsToMany User. Es decir, que un blog puede estar asociado a muchos usuarios y cada uno de estos estar asociado a varios blogs.

Yo quería identificar que algunos de esos usuarios son Administradores del Blog y otros son sólo Escritores. La idea es que cuando un usuario haga login y acceda a administrar sus blogs salgan sólo aquellos de los que es administrador (como es lógico, ¿no?).

Bien, para esto tenemos que "modelizar" la relación, o lo que es lo mismo, utilizar la join table como un modelo más. Es decir, si añadimos unos campos a la join table blogs_users podemos describir características de la relación entre cada usuario y cada blog. Por ejemplo, un campo admin, para indicar si el usuario es administrador o no del blog relacionado.

Por otro lado, y según el artículo de Cakebaker, también tenemos que modelizar la relación para conseguir la paginación de los resultados.

¿Cómo se hace esto?

Bien. Para crear el modelo tenemos que añadir un archivo app/models/blogs_user.php que contenga la definición básica (observa el nombre del modelo: viene del nombre de la tabla blogs_users, es CamelCased, empieza en mayúsculas y var singular):


En las definiciones de Blog y User tenemos que añadir la clave 'with' para indicar que las relaciones se harán mediante ese modelo. Así en app/models/blog.php

var $hasAndBelongsToMany = array(
'User' =>; array(
'className' => 'User',
'joinTable' => 'blogs_users',
'foreignKey' => 'blog_id',
'associationForeignKey' => 'user_id',
'unique' => true,
'with' => 'BlogsUser'
)
);


Y en app/models/user.php tres cuartos de lo mismo. Añade la clave 'with'.

Con esto ya tenemos la base del problema resuelto. Para hacer la paginación lo que tenemos que especificar en Controller::paginate() es que la haga usando la relación modelizada:

$this->paginate('BlogsUser')


He aquí un ejemplo, que estaría en app/controllers/blogs_controller.php:

function admin_index() {
if ($this->Auth->user('root') == 1) {
$this->set('blogs', $this->paginate());
} else {
$options = array(
'BlogsUser.admin' => 1,
'BlogsUser.user_id' => $this->Auth->user('id')
);
$this->set('blogs', $this->paginate('BlogsUser', $options) );
}
}

Hay que decir, antes de nada, que esta aplicación usa el Auth Componente para autentificar usuarios, de modo que podemos saber qué usuario está actualmente conectado llamando a $this->Auth->user().

En este caso, primero comprobamos si el usuario autentificado se trata de un root, en cuyo caso simplemente buscamos todos los Blogs sin ninguna condición en especial. 

Si no es así, establecemos las condiciones para localizar los blogs administrados por el usuario autentificado, datos que están en BlogsUser. Luego hacemos el paginate indicando que use el modelo BlogsUser. Podemos añadir las condiciones que deseemos en cualquiera de los tres modelos implicados.

No hay comentarios: