[英]PHP - How to build tree structure list?
所以,我的問題是,我想構建這兩個表的樹:
Parent table:
+-------+---------------+
| pr_id | parent_name |
+-------+---------------+
| 1 | p |
| 2 | p_0 |
| 3 | p_0_1 |
| 4 | q |
+-------+---------------+
Child table:
+-------+---------------+---------------------------+
| ch_id | pr_id | child_name |
+-------+---------------+---------------------------+
| 1 | 1 | p_0 |
| 2 | 1 | p_1 |
| 3 | 2 | p_0_0 |
| 4 | 2 | p_0_1 |
| 5 | 3 | p_0_1_0 |
| 6 | 3 | p_0_1_1 |
| 7 | 4 | q_0 |
| 8 | 4 | q_1 |
+-------+---------------+---------------------------+
樹應該看起來像:
任何人都可以幫助我解決遞歸問題嗎?
您不需要在數據庫中創建 2 個表,您可以僅從一個表中維護它,如下所示
+-------+---------------+---------------------------+
| id | parent_id | title |
+-------+---------------+---------------------------+
| 1 | 0 | Parent Page |
| 2 | 1 | Sub Page |
| 3 | 2 | Sub Sub Page |
| 4 | 0 | Another Parent Page |
+-------+---------------+---------------------------+
生成的數組將類似於
Array
(
[0] => Array
(
[id] => 1
[parent_id] => 0
[title] => Parent Page
[children] => Array
(
[0] => Array
(
[id] => 2
[parent_id] => 1
[title] => Sub Page
[children] => Array
(
[0] => Array
(
[id] => 3
[parent_id] => 1
[title] => Sub Sub Page
)
)
)
)
)
[1] => Array
(
[id] => 4
[parent_id] => 0
[title] => Another Parent Page
)
)
您需要使用以下遞歸函數來實現它
function buildTree(array $elements, $parentId = 0) {
$branch = array();
foreach ($elements as $element) {
if ($element['parent_id'] == $parentId) {
$children = buildTree($elements, $element['id']);
if ($children) {
$element['children'] = $children;
}
$branch[] = $element;
}
}
return $branch;
}
$tree = buildTree($rows);
算法非常簡單:
多循環和/或遞歸很簡單,但性能方面不是最佳解決方案。 它們對於較小的數據集完全沒問題,但這些規模不是很好。
更好的解決方案是每個項目只處理一次。
基本思想是您創建對象並使用對象的 byReference 特性。 為此,我創建了將所有內容“粘合”在一起的“ShadowObjects”,這些“ShadowObjects”稍后會填充數據。
你有一個通用的id
, parentId
列表
$list = [
['id' => 1, 'parent' => 0, 'otherData' => 'a'],
['id' => 2, 'parent' => 1, 'otherData' => 'b'],
['id' => 3, 'parent' => 1, 'otherData' => 'c'],
['id' => 4, 'parent' => 3, 'otherData' => 'd'],
['id' => 5, 'parent' => 3, 'otherData' => 'e'],
['id' => 6, 'parent' => 4, 'otherData' => 'f'],
['id' => 7, 'parent' => 1, 'otherData' => 'g'],
];
我們需要一個元素來保存和索引我們的樹元素,我喜歡為此使用 TreeClass。 class 將所有 TreeItems 保存在一個列表中,以唯一 ID 作為標識符,唯一 ID 對於快速訪問每個項目很重要。
我也喜歡 getRootItems() 方法來獲取所有 Root_Tree_Elements
class Tree
{
private array $items = [];
public function getItem(int $id): ?TreeItem
{
return $this->items[$id]??null;
}
public function setItem(TreeItem $item, int $id): void
{
$this->items[$id] = $item;
}
public function getRootItems(): array
{
$rootItems = [];
foreach ($this->items as $item) {
if ($item->getParent() === null)
$rootItems[] = $item;
}
return $rootItems;
}
}
是一個 object 代表一個“樹元素”它保存實際數據和父/子關系
class TreeItem
{
private ?TreeItem $parent = null;
private array $children = [];
private array $data = [];
public function setParent(?TreeItem $parent): void
{
$this->parent = $parent;
}
public function addChild(TreeItem $child): void
{
$this->children[] = $child;
}
public function setData(array $data): void
{
$this->data = $data;
}
}
這是創建 TreeStructure 的單個循環:
從列表中,我們知道存在具有parent_ID X
的父項以及ID Y
的項是它的子項的信息。
所以第一件事是我們詢問我們的樹是否已經存在 ID X 的 TreeItem(僅當 ID 不是 0 或 NULL 或“無效”時)。 如果沒有找到,我們創建這個樹項 NEW 並將其添加到樹中,即使我們不知道父項的所有數據,我們只知道父項的 ID,但這足以作為“SHADOW_ITEM”。
之后,我們查找實際的 TreeItem。 我們擁有該元素的所有信息。 我們查找它,因為我們有可能已經將它創建為“ShadowItem”,所以我們可以用實際的數據填充它。 請記住,TreeItem 和 Parent 是同一類型的對象。
同樣,如果 Item 不存在,則新建它並將新 Item 添加到 TreeList。 還將 ParentTreeItem 添加到當前 TreeItem。
然后我們用新的孩子填充我們的父元素(雙向關系父母知道孩子/孩子知道父母)。
我們不需要擔心將錯誤的孩子添加到父母甚至重復,因為我們只處理每個項目一次。
最后,我們將實際數據填充到每個項目。 因此,總共每個 ShadowItem 都包含真實數據,並且會丟失 ShadowItem 的 state。
$tree = new Tree();
foreach($list as $itemData) {
$id = $itemData['id'];
$pid = $itemData['parent'];
// if ZERO we have root element no parent exists
$parent = $tree->getItem($pid);
if ($pid > 0 && $parent === null) {
$parent = new TreeItem();
$tree->setItem($parent, $pid);
}
// create new tree item if not exists / otherwise get existing item
$item = $tree->getItem($id);
if ($item === null) {
$item = new TreeItem();
$item->setParent($parent);
$tree->setItem($item, $id);
}
if ($parent !== null) {
$parent->addChild($item);
}
$item->setData($itemData);
}
這是一個示例,並且“未優化”以使其更易於理解。 Tree 和 TreeItem 上還缺少許多東西以使實際可用,
隨意添加您喜歡的所有方法。
例如:
我經常在菜單生成中使用這種邏輯,在某處“緩存”/“存儲”完成樹也很容易。 一個簡單的序列化/反序列化就可以了:)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.