使用 PHP 将 Mysql Select 转换为独立于级别的目录结构的多维数组

Converting Mysql Select to Multidimensional Array of Directory Structure independent of levels using PHP

所以我有一个 select 语句,其中 returns 3 个来自数据库的值:

如果文件夹没有父文件夹,它 returns 父 ID 为空。

使用这些数据,我将结果分成了 2 个数组。

一个名为$tree,将用作"directory tree",首先将包含所有父文件夹的父ID设置为空... 另一个名为 $children,其中包含所有其他子文件夹。

通过这些,我试图创建一个多维 array($tree),我将使用它向使用 PHP/HTML 的用户显示文件结构。理想情况下,这将使用递归来允许不知道目录实际有多深。

我目前正在尝试以下操作但没有成功,考虑了一整天后我感到卡住了(函数 find Children 被函数 getDirTree 进一步调用):

    // should be recursive function. Takes 2 arrays as an argument.
    // returns $tree() which will be a multiple dimension array  
    function findChildren($tree, $children){
    // tree has 2 parents in first run
    foreach($tree as $folder){
        $tempArray = array();
        // children has 4
        foreach($children as $child){
            if ($child['parentId'] === $folder['folderId']){
                array_push($tempArray, $child);
                if(($childKey = array_search($child, $children)) !== false) {
                    unset($children[$childKey]);
                }
            }
        }
        if(($parentKey = array_search($tree, $folder)) !== false) {
            array_push($children[$parentKey],$tempArray);
        }
    }
// Need to have some sort of recursion in this function
//    if (!empty($children)){
//        findChildren($tree, $children);
//    }
}

// takes userId as int and returns a multi dimensional array representing the users folder structure.
function getDirTree($userId){
    global $mysqli;

    $children = array();
    $tree = array();

    if($folders = $mysqli->prepare("SELECT folders.id, folders.name, child_of_folder.parent_id
                                    FROM child_of_folder
                                    RIGHT JOIN folders
                                    ON  (child_of_folder.child_id = Folders.id)
                                    WHERE folders.user_id = ?;")) {
        // Bind the parameters...  s for String and the variable $name to be bound.
        if ($folders->bind_param("i", $userId)) {
            // execute the query
            if ($folders->execute()) {
                // store the results
                if($folders->store_result()){
                    // bind the results
                    if($folders->bind_result($folderId, $folderName, $parentId)) {
                        // Fetch the results
                        while ($folders->fetch()) {
                            if ($parentId === null) {
                                array_push($tree, array('folderId' => $folderId, 'folderName' => $folderName, 'parentId' => $parentId));
                            } else {
                                array_push($children, array('folderId' => $folderId, 'folderName' => $folderName, 'parentId' => $parentId));
                            }
                        }

                    } else {
                        $hasFolder = null;
                    }
                } else {
                    // if there were no values to store return false
                    $hasFolder = null;
                }
            } else {
                // if there was a problem executing the statement return null
                $hasFolder = null;
            }
        } else {
            // if there was a problem binding the statement return null
            $hasFolder = null;
        }
    } else {
        // if there was a problem preparing the statement return null
        $hasFolder = null;
    }

    if(!empty($children)){
        findChildren($tree, $children);
    }
    $folders->free_result();
    $mysqli->close();

    return $tree;   
}

$tree$children 在传递给 findChildren() 之前的输出:

Children Array Before findChildren

Array
(
    [0] => Array
        (
            [folderId] => 2
            [folderName] => home
            [parentId] => 1
        )

    [1] => Array
        (
            [folderId] => 3
            [folderName] => account
            [parentId] => 2
        )

    [2] => Array
        (
            [folderId] => 4
            [folderName] => bill
            [parentId] => 2
        )

    [3] => Array
        (
            [folderId] => 6
            [folderName] => work
            [parentId] => 2
        )

)

Tree Array Before findChildren

Array
(
    [0] => Array
        (
            [folderId] => 1
            [folderName] => work
            [parentId] => 
        )

    [1] => Array
        (
            [folderId] => 5
            [folderName] => hello
            [parentId] => 
        )

)

我认为这应该可行。我更改为 xml 结构而不是数组,因为 xml 结构在执行此递归时更容易处理,尤其是。 我做了一些假设,例如 $children 数组包含数据库中的所有子项。 我希望这就是您要找的东西。

 function getFolderStructure($userId) 
 {
   //global variable
   $xml = new DOMDocument(); 
   $xml->formatOutput = true; 

   $element = $xml->createElement($userId);
   $element->nodeValue = ''; 

   //get the top level folders. 
   getDirTree($userId);

   //goes through all the top level folders. 
   foreach($tree as $topLevelFolder)
   {
       findChildren($element, $topLevelFolder); 
   }

   $xml->appendChild($element); 

   return $xml; 

  }


 //prob need to use $this->xml 
 function findChildren(&$element, $folder)
 {
      if(is_array($folder) && isset($folder["folder_name"]) && isset($folder["folderId"]))
   {
       $folder = $xml->createElement($folder["folder_name"], ""); 

       $folderIdAttribute = $xml->createAttribute("FolderId"); 

       $folderIdAttribute->value = $folder["folderId"]; 

       $folder-appendChild($folderIdAttribute);

       if(isset($folder["parentId"]) && !empty($folder["parentId"]))
       {
           $parentIdAttribute = $xml->createAttribute("ParentId"); 

           $parentIdAttribute->value = $folder["parentId"]; 

           $folder->appendChild($folder);
       }


       foreach(findChildrenArray($folder['folderId']) as $child)
       {
           findChildren($folder, $child); 
       }

       $element->appendChild($folder); 
   }
}

function findChildrenArray($folderId)
{
   $retArray = array(); 

   foreach($children as $child)
   {
       if(isset($child["parentId"]) && $child["parentId"] == $folderId)
       {
           array_push($retArray, $child); 
       }
   }
}

我最终通过不将菜单项放在多维数组中解决了这个问题。

我正在寻找的最终结果是将数据库查询转换为 HTML,因此有了父数组和子数组,我创建了一个递归函数,将菜单项连接到带有标签的无序列表.

到目前为止,我已经测试了 3 个深度级别,但据我所知,它应该不会破坏更深的级别。

// returns a string value of an HTML built multi-level list ready to display in HTML
// requires 2 arrays. $tree array contains the top parent folders
//  and $children array contains all other folders which are not the top parents i.e. all the children
// and grandchildren and so on.  
function findChildren($tree, $children){
    $message = "";

    foreach($tree as $folder){
        $parent = array();
        if($folder['parentId'] === null) {
            $message .= "<li id='" . $folder['folderId'] . "'>" . $folder['folderName'] . " " . $folder['folderId'];
        }
        $i = 0;
        foreach ($children as $child) {
            if ($child['parentId'] === $folder['folderId']) {
                if (($childKey = array_search($child, $children)) !== false) {
                    if($i === 0){
                        $message .= "<ul>";
                    }
                    $message .= "<li>" . $child['folderName'] . " " . $child['folderId'];
                    $parent[$i] = $children[$childKey];
                    unset($children[$childKey]);
                    $message .= "</li>";
                }
                $i++;
            }
        }
        if(isset($parent[0])) {
            $message .= findChildren($parent, $children);
        }
        if($i > 0){
            $message .= "</ul>";
        }
        if($folder['parentId'] === null) {
            $message .= "</li>";
        }
    }
    return $message;
}

// Searches through DB for user folders and returns whatever findChildren() returns.
// requires a $userID as int
function getDirTree($userId){
    global $mysqli;

    $children = array();
    $tree = array();

    if($folders = $mysqli->prepare("SELECT folders.id, folders.name, child_of_folder.parent_id
                                            FROM child_of_folder
                                            RIGHT JOIN folders
                                            ON  (child_of_folder.child_id = Folders.id)
                                            WHERE folders.user_id = ?;")) {
        // Bind the parameters...  s for String and the variable $name to be bound.
        if ($folders->bind_param("i", $userId)) {
            // execute the query
            if ($folders->execute()) {
                // store the results
                if($folders->store_result()){
                    // bind the results
                    if($folders->bind_result($folderId, $folderName, $parentId)) {
                        // Fetch the results
                        while ($folders->fetch()) {
                            if ($parentId === null) {
                                array_push($tree, array('folderId' => $folderId, 'folderName' => $folderName, 'parentId' => $parentId));
                            } else {
                                array_push($children, array('folderId' => $folderId, 'folderName' => $folderName, 'parentId' => $parentId));
                            }
                        }
                    } else {
                        $hasFolder = null;
                    }
                } else {
                    // if there were no values to store return false
                    $hasFolder = null;
                }
            } else {
                // if there was a problem executing the statement return null
                $hasFolder = null;
            }
        } else {
            // if there was a problem binding the statement return null
            $hasFolder = null;
        }
    } else {
        // if there was a problem preparing the statement return null
        $hasFolder = null;
    }
    // Call findChildren
    $message = findChildren($tree, $children);

    // Add the surrounding block elements which would ideally be placed in the template to separate php logic and html 
    if ($message != ""){
        $message = "<ul>" . $message;
        $message .= "</ul>";
    } else {
        $message .= "No Folders Created";
    }

    $folders->free_result();
    $mysqli->close();

    return $message;
}