[英]PHP - How to build tree structure list?
So, my problem is, that I want to build a tree of these 2 tables:所以,我的问题是,我想构建这两个表的树:
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 |
+-------+---------------+---------------------------+
And the Tree should look like:树应该看起来像:
Can anybody help me out with a recursive solution??任何人都可以帮助我解决递归问题吗?
You do not need to create 2 tables in the database for this you can maintain it like below from one table only您不需要在数据库中创建 2 个表,您可以仅从一个表中维护它,如下所示
+-------+---------------+---------------------------+
| id | parent_id | title |
+-------+---------------+---------------------------+
| 1 | 0 | Parent Page |
| 2 | 1 | Sub Page |
| 3 | 2 | Sub Sub Page |
| 4 | 0 | Another Parent Page |
+-------+---------------+---------------------------+
The array generated will be like生成的数组将类似于
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
)
)
You need to use the below recursive function to achieve it您需要使用以下递归函数来实现它
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);
The algorithm is pretty simple:算法非常简单:
Multi Loop and/or Recursion are simple but performance wise not the best solution.多循环和/或递归很简单,但性能方面不是最佳解决方案。 they are totally fine for smaller datasets but these scale not very well.它们对于较小的数据集完全没问题,但这些规模不是很好。
a better solution is when every item is only processed one-time.更好的解决方案是每个项目只处理一次。
The Basic Idea is that you create Objects and use the byReference nature of the objects.基本思想是您创建对象并使用对象的 byReference 特性。 For that I create "ShadowObjects" that are "glue" everything together, these "ShadowObjects" are later filled with data.为此,我创建了将所有内容“粘合”在一起的“ShadowObjects”,这些“ShadowObjects”稍后会填充数据。
you have a generic id
, parentId
list你有一个通用的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'],
];
We need an Element that hold and index our Tree-Elements I like to use an TreeClass for that.我们需要一个元素来保存和索引我们的树元素,我喜欢为此使用 TreeClass。 That class Hold all TreeItems in a list with the unique-ID as identifier, the unique-ID is important to get quick access to every Item. class 将所有 TreeItems 保存在一个列表中,以唯一 ID 作为标识符,唯一 ID 对于快速访问每个项目很重要。
I also like the getRootItems() method to get all Root_Tree_Elements我也喜欢 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;
}
}
is an object that represent a "tree element" it holds the actual data and the parent / children relations是一个 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;
}
}
And here is The single Loop that creates the TreeStructure:这是创建 TreeStructure 的单个循环:
from the List we have the information that a parent exists with parent_ID X
and that a the item with the ID Y
is a child from it.从列表中,我们知道存在具有parent_ID X
的父项以及ID Y
的项是它的子项的信息。
So the first thing is we ask our Tree if there is an TreeItem with the id X is already existing (only if the ID is not 0 or NULL or something 'invalid').所以第一件事是我们询问我们的树是否已经存在 ID X 的 TreeItem(仅当 ID 不是 0 或 NULL 或“无效”时)。 If not found we create this Tree Item NEW and add this to the Tree, even we don't know all data for the parent, what we know is only the ID of the parent but that is enough as a 'SHADOW_ITEM'.如果没有找到,我们创建这个树项 NEW 并将其添加到树中,即使我们不知道父项的所有数据,我们只知道父项的 ID,但这足以作为“SHADOW_ITEM”。
after that, we look up for the actual TreeItem.之后,我们查找实际的 TreeItem。 We have all Information for that element.我们拥有该元素的所有信息。 We look it up cuz there is the chance that we create it already as a "ShadowItem", So we can fill it with actual DATA.我们查找它,因为我们有可能已经将它创建为“ShadowItem”,所以我们可以用实际的数据填充它。 Keep in mind that TreeItem and the Parent are the same type of Objects.请记住,TreeItem 和 Parent 是同一类型的对象。
Here again, if the Item not exists create it new and add the new Item to the TreeList.同样,如果 Item 不存在,则新建它并将新 Item 添加到 TreeList。 also Add the ParentTreeItem to the current TreeItem.还将 ParentTreeItem 添加到当前 TreeItem。
Then we fill up our Parent Element with the new Children (Bi-Directional Relation parent knows children / child knows Parent).然后我们用新的孩子填充我们的父元素(双向关系父母知道孩子/孩子知道父母)。
We don't need to worry about adding the wrong child to the parent or even duplicate, cuz we only process every item only ONE time.我们不需要担心将错误的孩子添加到父母甚至重复,因为我们只处理每个项目一次。
at the end we fill up the actual data to every item.最后,我们将实际数据填充到每个项目。 So that in total every ShadowItem contains the real data and loose the state of a ShadowItem.因此,总共每个 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);
}
This is an example and "NOT optimize" to make it more easy to understand.这是一个示例,并且“未优化”以使其更易于理解。 there also many things missing on the Tree and the TreeItem to make actual usable, Tree 和 TreeItem 上还缺少许多东西以使实际可用,
Feel free to add all methods you like.随意添加您喜欢的所有方法。
for example:例如:
I use this kind of logic often in Menu generations, it's also easy to "cache"/"store" the finish Tree somewhere.我经常在菜单生成中使用这种逻辑,在某处“缓存”/“存储”完成树也很容易。 a simple serialize / unserialize do the trick:)一个简单的序列化/反序列化就可以了:)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.