Las categorías en Prestashop se organizan internamente con el "nested set model" o modelo de conjunto anidado.
Es un algoritmo de recorrido de árbol o jerarquías y es una forma de almacenamiento de datos jerárquicas dentro de bases de datos relacionales.
Un árbol es una estructura de datos que contiene un número de nodos vinculados y un nodo padre puede conectarse a varios nodos secundarios, repitiéndose esta organización a través de varios niveles.
Cada nueva inserción, borrado o modificación implicar volver a numerar los nodos.
En versiones anteriores a Prestashop 1.6, se podían dejar en blanco los campos nLeft y nRight de la tabla ps_category, respetando el resto de campos imprescindibles como el id_parent y el level_depth, pero en la versión 1.6 si no se completan estos campos, las categorías no aparecerán en el frontoffice o parte pública, ni tampoco saldrán organizadas desde el backoffice en el formulario de alta de un artículo cuando se asocia a una categoría desde la opción de "Expand" o mostrar todo el árbol de categorías.
Cada elemento del árbol termina con dos valores, nLeft para la izquierda y nRight para la derecha, que se puede utilizar para identificarlos e indicar su relación con los otros elementos del árbol o estructura jerárquica.
Por ejemplo, si una subcategoría X tiene un valor de 2 y 15 para nLeft y nRight respectivamente todos los nodos que con valores superiores a 2 y los valores inferiores a 15 son dependientes de la subcategoría X.
Todo esto es un aspecto importante a tener en cuenta siempre que existan procesos externos de actualización o importación de artículos (en mi caso más de 2.000 categorías desde un ERP).
Si la inserción es puntual (ej: una importación de una estructura de catálogo), al modificar cualquier categoría desde el backoffice (aunque no se haya modificado realmente nada), se regeneran los valores de los campos nLeft y nRight, la función que se utiliza en este proceso es "regenerateEntireNtree"
El código de esta función "regenerateEntireNtree" está en classes/Category.php:
/**
* Re-calculate the values of all branches of the nested tree
*/
public static function regenerateEntireNtree()
{
$id = Context::getContext()->shop->id;
$id_shop = $id ? $id: Configuration::get('PS_SHOP_DEFAULT');
$categories = Db::getInstance()->executeS('
SELECT c.`id_category`, c.`id_parent`
FROM `'._DB_PREFIX_.'category` c
LEFT JOIN `'._DB_PREFIX_.'category_shop` cs
ON (c.`id_category` = cs.`id_category` AND cs.`id_shop` = '.(int)$id_shop.')
ORDER BY c.`id_parent`, cs.`position` ASC');
$categories_array = array();
foreach ($categories as $category)
$categories_array[$category['id_parent']]['subcategories'][] = $category['id_category'];
$n = 1;
if (isset($categories_array[0]) && $categories_array[0]['subcategories'])
Category::_subTree($categories_array, $categories_array[0]['subcategories'][0], $n);
}
protected static function _subTree(&$categories, $id_category, &$n)
{
$left = $n++;
if (isset($categories[(int)$id_category]['subcategories']))
foreach ($categories[(int)$id_category]['subcategories'] as $id_subcategory)
Category::_subTree($categories, (int)$id_subcategory, $n);
$right = (int)$n++;
Db::getInstance()->execute('
UPDATE '._DB_PREFIX_.'category
SET nleft = '.(int)$left.', nright = '.(int)$right.'
WHERE id_category = '.(int)$id_category.' LIMIT 1');
}
Referencias:
http://www.slickpalm.com/cual-es-el-modelo-anidado-set/
http://en.wikipedia.org/wiki/Nested_set_model