第一个循环在递归函数php中重启

时间:2015-06-08 11:59:27

标签: php mysql recursion pdo while-loop

我尝试创建一系列<ul><li>来创建目录/文件结构,以导航从dB中的表创建的某些文件。表(tb_lib_manual)包含文件和文件夹。

如果记录的fileID为空条目,则它是文件夹而不是文件。每条记录都有一个parentID来显示哪个文件夹是父文件夹,对于根目录中的文件和文件夹是0。

php代码因此:

class library_folders extends system_pageElement 
{
    private $html = '';
    private $i = 0;
    private $stmtArray = array();
    private $objectArray = array();

    function __construct() 
    {
        parent::__construct();
        $this->nextList();
    }


    function nextList($parentID = 0) 
    {
        $qSQL = 'SELECT * FROM tb_lib_manual WHERE parentID=:parentID';
        $stmtArray[$this->i] = $this->dbConnection->prepare($qSQL);
        $stmtArray[$this->i]->bindValue(':parentID', $parentID, PDO::PARAM_INT);
        $stmtArray[$this->i]->execute();
        if($stmtArray[$this->i]->rowCount() > 0)
        {
            $display ='';
            if($parentID != 0)
            {
                $display = ' style="display:none"';
            }
            $this->html .= '<ul' . $display . '>';
        }


        while ($this->objectArray[$this->i] = $stmtArray[$this->i]->fetchObject()) 
        {
            $this->html .= '<li>' . $this->objectArray[$this->i]->title;
            if($this->objectArray[$this->i]->fileID == null)
            {
                //we have a folder!
                $manualID = $this->objectArray[$this->i]->manualID;
                $this->i ++;
                $this->nextList($manualID);
                $this->i--;
            }
            $this->html .= '</li>';
        }


        if($stmtArray[$this->i]->rowCount() > 0)
        {
            $this->html .= '</ul>';
        }   

        echo $this->html;
    }   
function __destruct()
    {
        parent::__destruct();
    }

}

问题是当代码在调用自身之后返回到while循环时它重新启动循环而不是继续它停止的地方,导致子文件夹中的重复。有没有更好的方法来做到这一点,或者我做错了什么!?

表格如下:

enter image description here

输出如下: &#39;

  • 手册
  • 手册
  • 文件夹1
  • 美国宇航局练习
  • 手册
  • 手册
  • 文件夹1
  • 美国宇航局练习
&#39;

2 个答案:

答案 0 :(得分:1)

修正了代码,非常愚蠢的错误,在错误的地方回声......

class library_folders extends system_pageElement 
{
    private $html = '';
    private $i = 0;
    private $stmtArray = array();
    private $objectArray = array();

    function __construct() 
    {
        parent::__construct();

        $this->nextList();

        echo $this->html;

    }


    function nextList($parentID = 0) 
    {
        $qSQL = 'SELECT * FROM tb_lib_manual WHERE parentID=:parentID';
        //echo $this->i;        
        $stmtArray[$this->i] = $this->dbConnection->prepare($qSQL);
        $stmtArray[$this->i]->bindValue(':parentID', $parentID, PDO::PARAM_INT);
        $stmtArray[$this->i]->execute();
        if($stmtArray[$this->i]->rowCount() > 0)
        {
            $this->html .= '<ul>';
        }

        while ($this->objectArray[$this->i] = $stmtArray[$this->i]->fetchObject()) 
        {
            $this->html .= '<li>' . $this->objectArray[$this->i]->title;
            if($this->objectArray[$this->i]->fileID == null)
            {
                //we have a folder!
                $manualID = $this->objectArray[$this->i]->manualID;
                $this->i ++;
                $this->nextList($manualID);
                $this->i--;
            }
            $this->html .= '</li>';
        }


        if($stmtArray[$this->i]->rowCount() > 0)
        {
            $this->html .= '</ul>';
        }   

    }   

    function __destruct()
    {
        parent::__destruct();
    }

}

答案 1 :(得分:0)

是的,有更好的方法可以做到这一点。

如果你每次都获取整个树,你也可以将所有节点加载到一个大数组(对象)中,将每个节点的id作为索引,然后在数组上循环一次例如,通过

创建父引用
// create a fake root node to start your traversal later on
// only do this if you don't have a real root node 
// which I assume you don't
$root = (object) ["children" => []];

// loop over all nodes
foreach ($nodes as $node)
{
    // if the node has a parent node that is not root
    if ($node->parentId > 0)
    {
         // put it into it's parent's list of children
         $nodes[ $node->parentId ]->children[] = $node;
    }
    else
    {
         // otherwise put it into root's list of children
         $root->children[] = $node;
    }
}

复杂性:您执行一个查询,并且必须迭代所有节点一次。

为此,您的节点需要是对象。否则,对$node->children的每个分配都将创建您想要引用的已分配节点的副本。

<强> 如果您不想获取所有节点,则可以通过创建上一级别的节点ID列表逐级浏览树。

function fetchLevel ($nodes, $previousLevelIds)
{
    // get all children from the previous level's nodes
    // I assume $previousLevelIds to be an array of integers. beware of sql injection
    // I refrained from using prepared statements for simplicity
    $stmt = $pdo->query("SELECT id, parentId FROM nodes WHERE parentId IN (".implode(",",$previousLevelIds).")");

    // fetch nodes as instances of stdclass
    $currentLevelNodes = $stmt->fetchAll(PDO::FETCH_OBJ);

    $nextLevelIds = [];
    foreach ($currentLevelNodes as $node)
    {
         // ids for next level
         $nextLevelIds[] = $node->id;

         // parent <-> child reference
         $nodes[ $node->parentId ]->children[] = $node;
    }

    // fetch the next level only if we found any nodes in this level
    // this will stop the recursion
    if ($nextLevelIds)
        fetchLevel($nodes, $nextLevelIds);
}

// start by using a fake root again
$root = (object) ["id" => 0, "children" => []];
$nodes = [0 => $root];
fetchLevel($nodes, [0]);

// or start with a specific node in the tree
$node = $pdo->query("SELECT id, parentId FROM nodes WHERE id = 1337")->fetch(PDO::FETCH_OBJ);
$nodes = [$node->id => $node];
fetchLevel($nodes, [$node->id]);

// or a number of nodes which don't even have to 
// be on the same level, but you might fetch nodes multiple times
// if you it this way

复杂性:查询数&lt; =树的高度。您只迭代每个获取的节点一次。

要将树显示为html列表,您需要再次迭代:

class Foo {

    public $html;

    public function getList ($nodes)
    {
         // outer most ul
         $this->html = '<ul>';

         $this->recurseList($nodes);

         $this->html .= '</ul>';
    }

    protected function recurseList ($nodes)
    {
        foreach ($nodes as $node)
        {
            $this->html .= "<li><span>".$node->name."</span>";

            if ($node->children)
            {
                if ($node->parentId > 0)
                    $this->html .= '<ul style="display:none">';
                else
                    $this->html .= '<ul>';

                $this->recurseList($node->children);

                $this->html .= "</ul>";
            }

            $this->html .= "</li>";
        }
    }
}

一些不相关的评论:

  • 而不是硬编码的style="display:none"您只需使用ul li ul {display:none}等css规则隐藏根目录下的所有列表
  • 我拆分从数据库中获取数据并将数据转换为两个单独的脚本,这是使用MVC开发时的标准。如果您不想这样做,请连续运行脚本。
  • PHP本身是一个模板引擎,但考虑使用另一个模板引擎来显示你的html,如Smarty。我个人更喜欢smarty,因为它的语法更简单。
  • stackoverflow回答如何使用预准备语句将PHP数组绑定到MySQL IN运算符
  • 如果您需要提取子树,请考虑使用a special table结构来减少查询次数