递归函数php中的第一个循环重启
我正在尝试创建一系列递归函数php中的第一个循环重启,php,mysql,recursion,pdo,while-loop,Php,Mysql,Recursion,Pdo,While Loop,我正在尝试创建一系列和来创建目录/文件结构,以导航从数据库中的表创建的一些文件。该表(tb_lib_手册)包含文件和文件夹 如果一条记录的fileID项为空,那么它是一个文件夹而不是一个文件。每个记录都有一个parentID,用于显示哪个文件夹是父文件夹,对于根目录中的文件和文件夹,这是0 因此,php代码是: class library_folders extends system_pageElement { private $html = ''; private $i =
和
来创建目录/文件结构,以导航从数据库中的表创建的一些文件。该表(tb_lib_手册)包含文件和文件夹
如果一条记录的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();
}
}
类库文件夹扩展系统页面元素
{
私人$html='';
私人$i=0;
私有$stmtArray=array();
私有$objectArray=array();
函数_u构造()
{
父项::_构造();
$this->nextList();
}
函数nextList($parentID=0)
{
$qSQL='从tb_lib_手册中选择*,其中parentID=:parentID';
$stmtArray[$this->i]=$this->dbConnection->prepare($qSQL);
$stmtArray[$this->i]->bindValue(':parentID',$parentID,PDO::PARAM_INT);
$stmtArray[$this->i]->execute();
如果($stmtArray[$this->i]->rowCount()>0)
{
$display='';
如果($parentID!=0)
{
$display='style=“display:none”';
}
$this->html.='';
}
而($this->objectArray[$this->i]=$stmtArray[$this->i]->fetchObject())
{
$this->html.='。$this->objectArray[$this->i]->title;
if($this->objectArray[$this->i]->fileID==null)
{
//我们有一个文件夹!
$manualID=$this->objectArray[$this->i]->manualID;
$this->i++;
$this->nextList($manualID);
$this->i--;
}
$this->html.=' ';
}
如果($stmtArray[$this->i]->rowCount()>0)
{
$this->html.='';
}
echo$this->html;
}
函数_udestruct()
{
父项::_destruct();
}
}
问题是,当代码在调用自身后返回while循环时,它会重新启动循环,而不是在停止的地方继续,从而导致在子文件夹中重复。有没有更好的方法,或者我做错了什么
表如下所示:
输出如下:
“
- 一本手册
- 一本手册
- 文件夹1
- 美国航天局演习
- 一本手册
- 文件夹1
- 美国航天局演习
- ”是的,有更好的方法
如果每次都获取整个树,那么最好将所有节点加载到一个大数组中(对象),将每个节点的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; } }
的每个分配都将在需要引用的位置创建分配节点的副本 如果不想获取所有节点,可以通过从上一级创建节点ID列表,逐级遍历树$node->children
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
复杂性:查询数html='函数fetchLevel($nodes,$previousLevelIds) { //从上一级别的节点获取所有子级 //我假设$PreviousLevelId是一个整数数组。小心sql注入 //为了简单起见,我避免使用事先准备好的语句 $stmt=$pdo->query(“从parentId所在的节点中选择id、parentId(“.inplade”(“,”,$previousLevelIds)”); //获取节点作为stdclass的实例 $currentLevelNodes=$stmt->fetchAll(PDO::FETCH_OBJ); $nextLevelIds=[]; foreach($currentLevelNodes作为$node) { //下一级的ID $nextLevelIds[]=$node->id; //父子参照 $nodes[$node->parentId]->children[]=$node; } //仅当我们在此级别中找到任何节点时,才能获取下一级别 //这将停止递归 如果($nextLevelIds) fetchLevel($nodes,$nextLevelIds); } //从再次使用假根开始 $root=(object)[“id”=>0,“children”=>[]]; $nodes=[0=>$root]; fetchLevel($nodes,[0]); //或者从树中的特定节点开始 $node=$pdo->query(“从id=1337的节点中选择id,parentId”)->fetch(pdo::fetch_OBJ); $nodes=[$node->id=>$node]; fetchLevel($nodes,[$node->id]); //或者一些甚至不需要的节点 //位于同一级别,但可能会多次获取节点 //如果你这样做的话
- ';
$this->recurseList($nodes);
$this->html.='
- ”$node->name.”; 如果($node->children) { 如果($node->parentId>0) $this->html.=' ”; } } } 一些无关的评论:
- 与硬编码的
不同,您可以使用类似style=“display:none”
的css规则来隐藏根目录下的所有列表ul li ul{display:none}
- 我将从数据库中提取数据并将用于显示的数据转换为两个单独的脚本,这是使用开发时的标准。如果不想这样做,请连续运行脚本
- PHP本身是一个模板引擎,但是考虑使用另一个模板引擎来显示HTML。我个人更喜欢smarty,因为它的语法更简单李>
- 回答如何使用准备好的语句将PHP数组绑定到MySQL IN运算符 如果你需要取子树,经常考虑U
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>"; } } }
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(); } }
- ”是的,有更好的方法
如果每次都获取整个树,那么最好将所有节点加载到一个大数组中(对象),将每个节点的id作为索引,然后在数组上循环一次以创建父引用,例如