簡體   English   中英

創建樹結構所需的PHP遞歸幫助

[英]PHP recursion help needed to create a tree structure

這是我有的表結構:

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)
)

我正在使用遞歸函數從中創建菜單結構,但在這里失敗:

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;
 }

此方法是CodeIgniter模型類中模型的一部分。 有沒有更好的方法來創建樹?

是的,還有更好的方法。 所謂的改進的預序樹遍歷算法。 您可以通過谷歌搜索找到大量信息,而且我敢肯定堆棧也會溢出。

好處是您可以僅使用1個查詢來獲取整個子樹。 SELECTS將很快,但是修改會更重。

我認為您必須在函數調用中添加id作為參數。

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

否則,您每次都完全相同地調用該函數。

這是最好的例子。這是第一答案的更正形式。

 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;
}

該行:

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

一定是 :

$treeArray[$row['menuid']] = $this->categoriesTree($row['menuid']);
<?php
require_once ROOT_PATH . '/lib/dao/MySQLClass.php';
require_once ROOT_PATH . '/lib/confs/Conf.php';
/**
 * Generate HTML for multi-dimensional menu from MySQL database
 * with ONE QUERY and WITHOUT RECURSION
 * @author J. Bruni
 */
//print_r($_SESSION['symfony/user/sfUser/culture']);die;
class MenuBuilder
{
    /**
     * MySQL connection
     */
    var $conn;

    /**
     * Menu items
     */
    var $items = array();

    /**
     * HTML contents
     */
    var $html  = array();

        //var $culture = $_SESSION['symfony/user/sfUser/culture'];
        var $culture;
        var $columnName;
    /**
     * Create MySQL connection
     */
    function MenuBuilder()
    {
        $conf = new Conf();
            $db=new MySQLClass($conf);
           $this->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 (i.e., 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[] = '<div>';
        $this->html[] = '<ul id="qm0" class="qmmc">';

        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 ) . '</ul>';
                $this->html[] = str_repeat( "\t", ( count( $parent_stack ) + 1 ) * 2 - 1 ) . '</li>';
            }
            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<li><a class="qmparent" href="%2$s">%3$s</a>',
                    $tab,   // %1$s = tabulation
                    //$option['value']['sm_mnuitem_webpage_url'],   // %2$s = link (URL)
                    $url,
                    $option['value'][$this->columnName]   // %3$s = title
                );
                $this->html[] = $tab . "\t" . '<ul>';

                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<li><a target="rightMenu" href="%2$s">%3$s</a></li>',
                    str_repeat( "\t", ( count( $parent_stack ) + 1 ) * 2 - 1 ),   // %1$s = tabulation
                    $option['value']['sm_mnuitem_webpage_url'],   // %2$s = link (URL)
                    $option['value'][$this->columnName]   // %3$s = title
                );
                            }
                             }else{
                                 $this->html[] = sprintf(
                    '%1$s<li><a target="rightMenu" href="%2$s">%3$s</a></li>',
                    str_repeat( "\t", ( count( $parent_stack ) + 1 ) * 2 - 1 ),   // %1$s = tabulation
                    $option['value']['sm_mnuitem_webpage_url'],   // %2$s = link (URL)
                    $option['value'][$this->columnName]   // %3$s = title
                );
                             }
                        }
        }

        // HTML wrapper for the menu (close)
        $this->html[] = '</ul>';
                //$this->html[] = '</div>';

        return implode( "\r\n", $this->html );
    }
}





?>

使用基於關系數據庫的具有父/子關系的樹結構來構建菜單非常麻煩。 關系數據庫對於樹形結構而言非常糟糕 它們要求您編寫許多業務邏輯,以便以可讀格式表示數據。 要使用其他功能更新菜單,需要在該遞歸循環中添加……根據您希望菜單變得多么復雜,它可能變得非常冗長。 更不用說您最終將要緩存整個事情,因為在重負載下循環在計算上變得非常昂貴。 考慮一下,如果您有5個頂級菜單項,2個孩子,並且每個孩子自己有n個孩子,那么您將運行16條SQL語句。

我可以提供另一個解決方案: JSON 我曾經有一個像這樣的菜單表,現在我只是將它的JSON表示存儲在SQL數據庫中(盡管甚至可以將其緩存在內存/文件系統中)。 JSON菜單在空間上要緊湊得多,簡單地讀取是合乎邏輯的,並且不需要擺弄父母和孩子的ID。 它與PHP(json_encode / decode)將菜單轉換為本地數組的效果更好。 以及Javascript,例如在進行ajax調用以在應用程序中對菜單重新排序時,這一點很重要。 JSON擅長層次樹結構。 它還消除了跟蹤“菜單”的需要(因為數組順序是在內部指定的)

菜單格式示例如下:

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

如您所見,它確實非常容易提供附加功能,例如與菜單項綁定的“角色”。 添加這種功能不需要新的遞歸代碼,也不需要更改SQL模式。 確實更加靈活。

因此,不是真正回答問題,而是希望就我認為對這個問題的更好解決方案提供一些建議/見解。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM