name - Estructura de árbol PHP para categorías y subcategorías sin bucles de una consulta
get tag name wordpress (4)
Esto hace el trabajo:
$items = array(
(object) array(''id'' => 42, ''parent_id'' => 1),
(object) array(''id'' => 43, ''parent_id'' => 42),
(object) array(''id'' => 1, ''parent_id'' => 0),
);
$childs = array();
foreach($items as $item)
$childs[$item->parent_id][] = $item;
foreach($items as $item) if (isset($childs[$item->id]))
$item->childs = $childs[$item->id];
$tree = $childs[0];
print_r($tree);
Esto funciona clasificando primero las categorías por parent_id. Luego, para cada categoría, solo tenemos que establecer category->childs
a childs[category->id]
, ¡y se construye el árbol!
Entonces, ahora $tree
es el árbol de categorías. Contiene una matriz de elementos con parent_id = 0, que a su vez contienen una matriz de sus hijos, que a su vez ...
Salida de print_r($tree)
:
stdClass Object
(
[id] => 1
[parent_id] => 0
[childs] => Array
(
[0] => stdClass Object
(
[id] => 42
[parent_id] => 1
[childs] => Array
(
[0] => stdClass Object
(
[id] => 43
[parent_id] => 42
)
)
)
)
)
Así que aquí está la función final:
function buildTree($items) {
$childs = array();
foreach($items as $item)
$childs[$item->parent_id][] = $item;
foreach($items as $item) if (isset($childs[$item->id]))
$item->childs = $childs[$item->id];
return $childs[0];
}
$tree = buildTree($items);
Aquí está la misma versión, con matrices, que es un poco complicado ya que necesitamos jugar con referencias (pero funciona igual de bien):
$items = array(
array(''id'' => 42, ''parent_id'' => 1),
array(''id'' => 43, ''parent_id'' => 42),
array(''id'' => 1, ''parent_id'' => 0),
);
$childs = array();
foreach($items as &$item) $childs[$item[''parent_id'']][] = &$item;
unset($item);
foreach($items as &$item) if (isset($childs[$item[''id'']]))
$item[''childs''] = $childs[$item[''id'']];
unset($item);
$tree = $childs[0];
Entonces, la versión de matriz de la función final:
function buildTree($items) {
$childs = array();
foreach($items as &$item) $childs[$item[''parent_id'']][] = &$item;
unset($item);
foreach($items as &$item) if (isset($childs[$item[''id'']]))
$item[''childs''] = $childs[$item[''id'']];
return $childs[0];
}
$tree = buildTree($items);
Intento crear una lista de categorías con cualquier cantidad de subcategorías, donde las subcategorías también pueden tener sus propias subcategorías.
He seleccionado todas las categorías del db Mysql, los gatos están en una lista de matriz asociada estándar, cada categoría tiene una identificación, nombre, parentid donde el parentid es 0 si es de nivel superior.
Básicamente, quiero poder tomar la matriz de gatos de un solo nivel y convertirla en una estructura de matriz multidimensional donde cada categoría puede tener un elemento que contenga una matriz de subcampos.
Ahora, puedo lograrlo fácilmente haciendo un bucle de una consulta para cada categoría, pero esto está lejos de ser ideal, estoy tratando de hacerlo sin hits adicionales en el db.
Entiendo que necesito una función recursiva para esto. ¿Alguien puede señalarme en la dirección correcta para esta estructura de estilo de árbol?
Aclamaciones
Puede buscar todas las categorías a la vez.
Supongamos que tiene un resultado plano de la base de datos, como este:
$categories = array(
array(''id'' => 1, ''parent'' => 0, ''name'' => ''Category A''),
array(''id'' => 2, ''parent'' => 0, ''name'' => ''Category B''),
array(''id'' => 3, ''parent'' => 0, ''name'' => ''Category C''),
array(''id'' => 4, ''parent'' => 0, ''name'' => ''Category D''),
array(''id'' => 5, ''parent'' => 0, ''name'' => ''Category E''),
array(''id'' => 6, ''parent'' => 2, ''name'' => ''Subcategory F''),
array(''id'' => 7, ''parent'' => 2, ''name'' => ''Subcategory G''),
array(''id'' => 8, ''parent'' => 3, ''name'' => ''Subcategory H''),
array(''id'' => 9, ''parent'' => 4, ''name'' => ''Subcategory I''),
array(''id'' => 10, ''parent'' => 9, ''name'' => ''Subcategory J''),
);
Puede crear una función simple que convierte esa lista plana en una estructura, preferiblemente dentro de una función. Uso pass-by-reference para que haya solo una matriz por categoría y no múltiples copias de la matriz para una categoría.
function categoriesToTree(&$categories) {
Un mapa se usa para buscar categorías rápidamente. Aquí, también creé una matriz ficticia para el nivel "raíz".
$map = array(
0 => array(''subcategories'' => array())
);
Agregué otro campo, subcategorías, a cada matriz de categoría y lo agregué al mapa.
foreach ($categories as &$category) {
$category[''subcategories''] = array();
$map[$category[''id'']] = &$category;
}
Pasando por cada categoría nuevamente, agregándose a la lista de subcategorías de sus padres. La referencia es importante aquí; de lo contrario , las categorías ya agregadas no se actualizarán cuando haya más subcategorías.
foreach ($categories as &$category) {
$map[$category[''parent'']][''subcategories''][] = &$category;
}
Finalmente, regrese las subcategorías de esa categoría ficticia que se refieren a todas las categorías de nivel superior._
return $map[0][''subcategories''];
}
Uso:
$tree = categoriesToTree($categories);
Y aquí está el código en acción en Codepad .
Tuve el mismo problema y lo resolví de esta manera: obtengo las filas cat de DB y para cada categoría raíz, árbol de compilación, comenzando con el nivel (profundidad) 0. Puede que no sea la solución más eficiente, pero funciona para mí.
$globalTree = array();
$fp = fopen("/tmp/taxonomy.csv", "w");
// I get categories from command line, but if you want all, you can fetch from table
$categories = $db->fetchCol("SELECT id FROM categories WHERE parentid = ''0''");
foreach ($categories as $category) {
buildTree($category, 0);
printTree($category);
$globalTree = array();
}
fclose($file);
function buildTree($categoryId, $level)
{
global $db, $globalTree;
$rootNode = $db->fetchRow("SELECT id, name FROM categories WHERE id=?", $categoryId);
$childNodes = $db->fetchAll("SELECT * FROM categories WHERE parentid = ? AND id <> ? ORDER BY id", array($rootNode[''id''], $rootNode[''id'']));
if(count($childNodes) < 1) {
return 0;
} else {
$childLvl = $level + 1;
foreach ($childNodes as $childNode) {
$id = $childNode[''id''];
$childLevel = isset($globalTree[$id])? max($globalTree[$id][''depth''], $level): $level;
$globalTree[$id] = array_merge($childNode, array(''depth'' => $childLevel));
buildTree($id, $childLvl);
}
}
}
function printTree($categoryId) {
global $globalTree, $fp, $db;
$rootNode = $db->fetchRow("SELECT id, name FROM categories WHERE id=?", $categoryId);
fwrite($fp, $rootNode[''id''] . " : " . $rootNode[''name''] . "/n");
foreach ($globalTree as $node) {
for ($i=0; $i <= $node[''depth'']; $i++) {
fwrite($fp, ",");
}
fwrite($fp, $node[''id''] " : " . $node[''name''] . "/n");
}
}
PD. Soy consciente de que OP está buscando una solución sin consultas de DB, pero esta implica la recursión y ayudará a cualquiera que encuentre esta solución en busca de soluciones recursivas para este tipo de preguntas y no le importen las consultas de DB.
Vea el método:
function buildTree(array &$elements, $parentId = 0) {
$branch = array();
foreach ($elements as $element) {
if ($element[''parent_id''] == $parentId) {
$children = buildTree($elements, $element[''id'']);
if ($children) {
$element[''children''] = $children;
}
$branch[$element[''id'']] = $element;
}
}
return $branch;
}