Ademas de estudiar y jugar al Dynasty Warriors 5, me he vuelto a poner con la programación del nuevo CMS. Aun estoy esperando que Ally termine con los diseños de su weblog y el mio, pero yo ya estoy a saco con la programación de la sección de administración. Aun falta para que este todo listo, pero espero tenerlo todo para la Euskal. Últimamente he codificado las categorías de los posts. Ahora ya no habrá unas categorías fijas guardadas en el código php, sino que serán accesibles a través de un complejo árbol extensible (categorías y sub-categorías infinitas) dentro de una tabla de mysql.
Para complacer un poco a BenKo voy a explicar un poco como he creado todo el tema del árbol. No es fácil, así que cuidado con perderse.
La tabla en la base de datos solo tiene 6 campos:
id -> Identificador
parent -> Nodo padre (categoria de la que depende)
title -> Nombre de la categoria
lft -> lo explicare luego
rgt -> tambien lo explicare luego xD
active -> 1 para categoria activa, 0 para inactiva

Los datos fundamentales son el id, el id del padre, y lft/rgt. ¿Pero qué demonios son lft y rgt? Vamos a verlo con un ejemplo:
En la imagen podéis ver un árbol de ejemplo, con sus valores lft en rojo y rgt en verde. lft y rgt son índices que nos ayudan a comprender la estructura del árbol y a mostrarlo con una sola consulta a la base de datos. Si solo guardáramos el valor de
pid y
pid padre, tendríamos que recorrer todo el árbol con llamadas recursivas, lo que sería una perdida absoluta de recursos y tiempo.
¿Qué sub-categorías pertenecen a Informática? Fácil, ¿no?. Solo hay que preguntarle a la base de datos que categorías tienen su lft entre 10 y 21, es decir:
SELECT id FROM nombretabla WHERE lft BETWEEN 10 AND 21 ORDER BY lft ASC;
Sencillo, ¿verdad? Con esos IDs devueltos podemos borrar, buscar o hacer lo que necesitemos. Voy a explicar algunas operaciones:
Imprimir arbol a partir del nodo xxx
function print_tree
($root=
1) { // Obtenemos de la BD el valor lft y rgt del nodo $root $sql =
"SELECT lft, rgt FROM `tabla_categ` WHERE id='$root' ";
$result =
mysql_query($sql);
$row =
mysql_fetch_array($result);
$lft =
$row['lft'];
$rgt =
$row['rgt'];
$right =
array();
// obtenemos todos los hijos del nodo $root $sql =
"SELECT id, title, lft, rgt, active FROM `tabla_categ` WHERE `lft` BETWEEN $lft AND $rgt ORDER BY lft ASC";
$result =
mysql_query($sql);
while ($row =
mysql_fetch_array($result)) { // solo comprobar el vector $right si existe) if (count($right)>
0) { while ($right[count($right)-
1]<
$row['rgt']) { array_pop($right);
} } if($row['lft']==
$lft) $str =
str_repeat('',
count($right));
else $str =
str_repeat('     ',
count($right)-
1).
'|--> ';
echo $str .
$row['title'].
" ";
if(!
$row['active']) echo " (Inactiva)";
echo "\n";
$right[] =
$row['rgt'];
} }
Reconstruir valores rgt
Muy útil porque después de mover valores, tendremos los
lft correctos, pero no los
rgt. Esta función recursiva reescribirá los valores
rgt (una gran ayuda

)
function rebuild_tree
($parent,
$left) { if(!
is_numeric($parent) || !
is_numeric($left)) die();
// el valor rgt de un nodo final es lft+1 $right =
$left +
1;
// obtener todos los hijos de ese nodo $sql =
"SELECT id, title FROM `tabla_categ` WHERE parent='$parent' ORDER BY title ASC";
$result =
mysql_query($sql);
while ($row =
mysql_fetch_array($result)) { // Viva la recursividad!!! xD $right = rebuild_tree
($row['id'],
$right);
} $sql =
"UPDATE `tabla_categ` SET `lft` = '$left', `rgt` = '$right' WHERE `id`='$parent'";
$result =
mysql_query($sql);
// devolver right+1 return $right+
1;
}
Mover un nodo
// $idOrigen -> El nodo a mover// $idDentroDe -> Dentro de que nodo (=nodo padre)// queremos moverlo function moveNode
($idOrigen,
$idDentroDe) { // obtenemos lft y rgt del elemento a mover $sql =
"SELECT `lft`,`rgt` FROM `tabla_categ` WHERE `id`='$idOrigen'";
$result =
mysql_query($sql);
$row =
mysql_fetch_array($result);
$lftorigen =
$row['lft'];
$rgtorigen =
$row['rgt'];
// obtenemos lft del padre destino $sql =
"SELECT `lft` FROM `tabla_categ` WHERE `id`='$idDentroDe'";
$result =
mysql_query($sql);
$row =
mysql_fetch_array($result);
// el nodo movido terminara teniendo lft // igual a la del padre + 1 $lftdestino =
$row['lft']+
1;
// $distancia a mover $distancia =
$lftdestino -
$lftorigen;
// amplitud del intervalo a mover // (el nodo a mover puede tener hijos!) $amplitud =
($rgtorigen -
$lftorigen) +
1;
(if($distancia >
0) { // desplazamos nodos para dejar hueco en destino $sql =
"UPDATE `tabla_categ` SET `lft`= 'lft+$amplitud', `rgt`= 'rgt+$amplitud' WHERE `lft` >=$lftdestino";
$result =
mysql_query($sql);
// cambiamos lft y rgt a los del destino $sql =
"UPDATE `tabla_categ` SET `lft`= 'lft+$distancia', `rgt`= 'rgt+$distancia' WHERE `lft` BETWEEN $lftorigen AND $rgtorigen";
$result =
mysql_query($sql);
// quitamos el hueco dejado en el origen // tras el desplazamiento $sql =
"UPDATE `tabla_categ` SET `lft`= 'lft-$amplitud', `rgt`= 'rgt-$amplitud' WHERE `lft` >$rgtorigen";
$result =
mysql_query($sql);
} elseif($distancia<
0) { // desplazamos nodos para dejar hueco en destino $sql =
"UPDATE `tabla_categ` SET `lft`= 'lft+$amplitud', `rgt`= 'rgt+$amplitud' WHERE `lft` >=$lftdestino";
$result =
mysql_query($sql);
// cambiamos lft yrgt a los del destino // NOTA: en este caso los lft y rgt han sido // desplazados por el UPDATE anterior $sql =
"UPDATE `tabla_categ` SET `lft`= 'lft-".
($distancia+
$amplitud).
"', `rgt`= 'rgt-".
($distancia+
$amplitud).
"' WHERE `lft` BETWEEN ".
($lftorigen+
$amplitud).
" AND ".
($rgtorigen+
$amplitud);
$result =
mysql_query($sql);
// quitamos el hueco dejado en el origen // tras el desplazamiento $sql =
"UPDATE `tabla_categ` SET `lft`= 'lft-$amplitud', `rgt`= 'rgt-$amplitud' WHERE `lft` >".
($rgtorigen+
$amplitud);
$result =
mysql_query($sql);
} $sql =
"UPDATE `tabla_categ` SET `parent`='$idDentroDe' WHERE `id`=".
$idOrigen;
$result =
mysql_query($sql);
// ¡¡IMPORTANTE!! rebuild_tree
(1,
1);
}
¿Como lo enlazamos con los mensajes? Bien, dependiendo de la base de datos, y de como guardemos los mensajes, cada uno de estos tendrá asociado un id de categoría. Si estamos dispuestos a hacer uso de otra tabla, podemos guardar en otra tabla:
id -> Índice único de la tabla (no nos interesa)
id_msg -> Identificador de la entrada del weblog
id_categ -> Identificador de categoría
¿Qué conseguimos con esto? Pues que con una consulta sencilla podamos obtener todas las posibles categorías a las que pertenezca un mensaje. Un poco complicado, ¿no? Imaginad este trozo de tabla:
|----------------------|
| id | id_msg | id_cat |
|----------------------|
| 1 | 100 | 6 |
| 2 | 100 | 2 |
| 3 | 101 | 4 |
| 4 | 102 | 2 |
|----------------------|
Es fácil hacer una consulta que nos diga a que categorías pertenece el mensaje 100, ¿verdad? Gracias a esta estructura de árbol, también sabemos que todos los mensajes de KDE pertenecen también a Linux y a su vez a Informática, por lo que al realizar la búsqueda en la base de datos de un mensaje con categoría informática, podríamos buscar (una consulta compleja pero posible) todos los mensajes que tengan una categoría cuyo valor lft este entre 10 y 21 (el lft y el rgt de Informática).
El mayor problema con el que me he encontrado es a la hora de borrar categorías. ¿Que pasa con los mensajes huérfanos (=sin categoría)? No me apetece comerme la cabeza con este asunto de poca importancia, así que lo que he decidido es que no se pueden borrar categorías. Se les puede cambiar el nombre, mover y se pueden desactivar (al desactivar un nodo, se desactiva también toda la rama que cuelga de el), pero no se pueden borrar (para evitar un caos con los IDs). Si nos sobra una categoría podemos cambiarle el nombre, el padre y la posición y emplearla para otra cosa (habrá que cambiar las categorías de los mensajes que dependan de esa categoría, pero es un problema menor).Creo que me ha quedado un tutorial un poco complicado... espero que alguien pueda entender algo. Voy a rehacerlo unas cuantas veces para ver si queda mas útil....
Comentarios