Ayuda de recurrencia PHP necesaria para crear una estructura de árbol

Aquí hay una estructura de tabla que tengo:

CREATE TABLE menu ( menuid int(11) NOT NULL AUTO_INCREMENT, menuname varchar(100) NOT NULL DEFAULT '', menulink varchar(100) NOT NULL DEFAULT '', menuparentId int(11) NOT NULL DEFAULT '0', menuhasChild smallint(1) NOT NULL DEFAULT '0', menustatus smallint(1) NOT NULL DEFAULT '1', menuorder int(11) NOT NULL DEFAULT '0', PRIMARY KEY (menuid) ) 

Estoy usando una función recursiva para crear una estructura de menú a partir de esto y fallar aquí:

 function categoriesTree($id=0){ $s = "SELECT * FROM menu WHERE menuparentId = '".$id."' ORDER BY menuorder, menuid "; $rid = $this->db->query($s)->result_array(); $treeArray = array(); foreach($rid as $row){ $treeArray[$row['menuid']] = $row; if($row['menuhasChild']==1){ $treeArray[$row['menuid']] = $this->categoriesTree(); //results in Fatal error: Maximum function nesting level of '100' reached, aborting! } } retrun $treeArray; } 

Este método es parte de un modelo en la clase de modelo CodeIgniter. ¿Hay una mejor manera de crear el árbol?

Creo que tienes que agregar el id como parámetro en tu llamada de función.

 $this->categoriesTree($row['menuid']) 

De lo contrario, llame a la función exactamente igual siempre.

Sí, hay una manera mucho mejor. El denominado algoritmo de recorrido de árbol preordenado modificado. Puede encontrar mucha información buscando en Google esto, y estoy seguro de que también se desbordará la stack.

Los beneficios son que puede obtener un subárbol completo con solo 1 consulta. SELECTS será rápido, pero las modificaciones son más pesadas.

Este es el mejor ejemplo. Esta es la forma corregida de la primera respuesta.

  function categoriesTree($id=0) { $s = "SELECT * FROM design_menu WHERE menuparentId = '" . $id . "' ORDER BY menuorder, menuid "; $rid = $this->db->query($s)->result_array(); $treeArray = array(); foreach ($rid as $row) { $treeArray[$row['menuid']] = $row; if ($row['menuhasChild'] == 1) { $treeArray[$row['menuname']] = $this->categoriesTree($row['menuid']); //results in Fatal error: Maximum function nesting level of '100' reached, aborting! } } return $treeArray; } 

La línea :

 $treeArray[$row['menuid']] = $this->categoriesTree(); 

debe ser :

 $treeArray[$row['menuid']] = $this->categoriesTree($row['menuid']); 
 conn = mysql_connect($db->myHost .':'.$db->myHostPort, $db->userName, $db->userPassword); $this->culture=$_SESSION['language']; } /** * Perform MySQL query and return all results */ function fetch_assoc_all( $sql ) { if($this->culture=="en"){ $this->columnName='sm_mnuitem_name'; }else{ $this->columnName='sm_mnuitem_name_'.$this->culture; } //die(print_r($_SESSION)); if($_SESSION['user']=="USR001"){ $query="SELECT sm_mnuitem_id, sm_mnuitem_parent, ".$this->columnName.", sm_mnuitem_webpage_url, sm_mnuitem_position FROM hs_hr_sm_mnuitem ORDER BY sm_mnuitem_parent, sm_mnuitem_position;"; } else{ $query="select * from hs_hr_sm_mnuitem m left join hs_hr_sm_mnucapability c on m.sm_mnuitem_id=c.sm_mnuitem_id left join hs_hr_users u on u.sm_capability_id=c.sm_capability_id where u.id='".$_SESSION['user']."' ORDER BY m.sm_mnuitem_parent, m.sm_mnuitem_position;"; } //$result = mysql_query("SELECT sm_mnuitem_id, sm_mnuitem_parent, ".$this->columnName.", sm_mnuitem_webpage_url, sm_mnuitem_position FROM hs_hr_sm_mnuitem ORDER BY sm_mnuitem_parent, sm_mnuitem_position;",$this->conn); $result = mysql_query($query,$this->conn); if ( !$result ){ return false; } $assoc_all = array(); while( $fetch = mysql_fetch_assoc( $result ) ){ $assoc_all[] = $fetch; } //die(print_r($assoc_all)); mysql_free_result( $result ); return $assoc_all; } /** * Get all menu items from database */ function get_menu_items() { // Change the field names and the table name in the query below to match tour needs $sql = 'SELECT sm_mnuitem_id, sm_mnuitem_parent, sm_mnuitem_name, sm_mnuitem_webpage_url, sm_mnuitem_position FROM hs_hr_sm_mnuitem ORDER BY s_mnuitem_parent, sm_mnuitem_position;'; return $this->fetch_assoc_all( $sql ); } /** * Build the HTML for the menu */ function get_menu_html( $root_id = 0 ) { $this->html = array(); $this->items = $this->get_menu_items(); //print_r($this->items);die(""); foreach ( $this->items as $item ) $children[$item['sm_mnuitem_parent']][] = $item; // loop will be false if the root has no children (ie, an empty menu!) $loop = !empty( $children[$root_id] ); // initializing $parent as the root $parent = $root_id; $parent_stack = array(); // HTML wrapper for the menu (open) //$this->html[] = '
'; $this->html[] = '
    '; while ( $loop && ( ( $option = each( $children[$parent] ) ) || ( $parent > $root_id ) ) ) { if ( $option === false ) { $parent = array_pop( $parent_stack ); // HTML for menu item containing childrens (close) $this->html[] = str_repeat( "\t", ( count( $parent_stack ) + 1 ) * 2 ) . '
'; $this->html[] = str_repeat( "\t", ( count( $parent_stack ) + 1 ) * 2 - 1 ) . ''; } elseif ( !empty( $children[$option['value']['sm_mnuitem_id']] ) ) { $tab = str_repeat( "\t", ( count( $parent_stack ) + 1 ) * 2 - 1 ); // HTML for menu item containing childrens (open) $url=""; if($option['value']['sm_mnuitem_webpage_url']=="#"){ $url="javascript:void(0);"; }else{ $url=$option['value']['sm_mnuitem_webpage_url']; } $this->html[] = sprintf( '%1$s
  • %3$s', $tab, // %1$s = tabulation //$option['value']['sm_mnuitem_webpage_url'], // https://stackoverflow.com/questions/5961377/php-recursion-help-needed-to-create-a-tree-structure/%2$s = link (URL) $url, $option['value'][$this->columnName] // %3$s = title ); $this->html[] = $tab . "\t" . '
      '; array_push( $parent_stack, $option['value']['sm_mnuitem_parent'] ); $parent = $option['value']['sm_mnuitem_id']; } else{ // HTML for menu item with no children (aka "leaf") if($_SESSION['user']!="USR001"){ if($option['value']['sm_mnuitem_webpage_url']!="#"){ $this->html[] = sprintf( '%1$s
    • %3$s
    • ', str_repeat( "\t", ( count( $parent_stack ) + 1 ) * 2 - 1 ), // %1$s = tabulation $option['value']['sm_mnuitem_webpage_url'], // https://stackoverflow.com/questions/5961377/php-recursion-help-needed-to-create-a-tree-structure/%2$s = link (URL) $option['value'][$this->columnName] // %3$s = title ); } }else{ $this->html[] = sprintf( '%1$s
    • %3$s
    • ', str_repeat( "\t", ( count( $parent_stack ) + 1 ) * 2 - 1 ), // %1$s = tabulation $option['value']['sm_mnuitem_webpage_url'], // https://stackoverflow.com/questions/5961377/php-recursion-help-needed-to-create-a-tree-structure/%2$s = link (URL) $option['value'][$this->columnName] // %3$s = title ); } } } // HTML wrapper for the menu (close) $this->html[] = '
    '; //$this->html[] = '
  • '; return implode( "\r\n", $this->html ); } } ?>

    Crear un menú usando estructuras de árbol con relaciones padre / hijo basadas en una base de datos relacional es muy engorroso. Las bases de datos relacionales son terribles para las estructuras de los árboles. Requieren que escriba mucha lógica empresarial solo para representar sus datos en un formato legible. Para actualizar el menú con funcionalidad adicional, es necesario agregarlo a ese bucle recursivo … puede ser muy complicado dependiendo de qué tan complejo desee que se convierta su menú. Sin mencionar que eventualmente querrás almacenar todo en caché porque el ciclo se vuelve bastante costoso computacionalmente bajo cargas pesadas. Piénselo, si tiene 5 elementos de menú de nivel superior, 2 hijos y cada niño tiene n hijos, estará ejecutando 16 sentencias de SQL.

    Puedo ofrecer otra solución: JSON . Solía ​​tener una tabla de menús como esta, y ahora solo guardo una representación JSON de ella en la base de datos SQL (aunque incluso esto podría almacenarse en caché en la memoria / sistema de archivos). El menú JSON es mucho más compacto en términos de espacio, es lógico simplemente leer y no requiere tocar el violín con las identificaciones padre e hijo. Funciona mucho mejor con PHP (json_encode / decode) convirtiendo el menú en una matriz nativa. Así como con Javascript, que es importante si, por ejemplo, está haciendo llamadas ajax para reordenar su menú en su aplicación. Las estructuras de árbol jerárquicas son lo que JSON es bueno. También elimina la necesidad de realizar un seguimiento de su “orden de menú” (porque el orden de la matriz se especifica intrínsecamente)

    Un formato de menú de ejemplo es el siguiente:

     { ["en": "Home", "fr": "Accueil"], ["en": "Settings", "fr": "Paramètres", "child": { ["en": "Email", "fr": "Email", "role": "EmailUser"] } } 

    Como puede ver, ofrece una funcionalidad adicional muy fácil, como un “papel” vinculado a un elemento del menú. Agregar este tipo de funcionalidad no requiere un nuevo código recursivo o cambios en su esquema de SQL. Es realmente mucho más flexible.

    Por lo tanto, no estoy respondiendo realmente la pregunta, pero es de esperar que proporcione algunos consejos / ideas sobre lo que siento es una mejor solución para este problema.