PHP - 从平面数组制作嵌套树状菜单结构
PHP - Making a nested tree menu structure from a flat array
我正在根据从 WP 数据库获得的响应制作一个嵌套菜单数组。在 corcel package 的帮助下,我从 Laravel 中控制器中的 WP 获取数据,然后制作一个包含菜单数据的数组,该数组现在是一层深。因此,当菜单 link 有子菜单 links 时,数组如下所示:
{
"Hjem": {
"ID": 112,
"title": "Hjem",
"slug": "hjem",
"url": "http://hivnorge.app/?p=112",
"status": "publish",
"main_category": "Hovedmeny",
"submenus": [
{
"ID": 129,
"title": "Lorem ipsum",
"slug": "lorem-ipsum",
"url": "http://hivnorge.app/?p=129",
"status": "publish",
"main_category": "Nyheter"
}
]
},
"Nytt test innlegg": {
"ID": 127,
"title": "Nytt test innlegg",
"slug": "nytt-test-innlegg",
"url": "http://hivnorge.app/?p=127",
"status": "private",
"main_category": "Nyheter",
"submenus": [
{
"ID": 125,
"title": "Test innlegg",
"slug": "test-innlegg",
"url": "http://hivnorge.app/?p=125",
"status": "publish",
"main_category": "Nyheter"
},
{
"ID": 129,
"title": "Lorem ipsum",
"slug": "lorem-ipsum",
"url": "http://hivnorge.app/?p=129",
"status": "publish",
"main_category": "Nyheter"
}
]
},
"Prosjektsamarbeidets verdi": {
"ID": 106,
"title": "Prosjektsamarbeidets verdi",
"slug": "prosjektsamarbeidets-verdi",
"url": "http://hivnorge.no.wordpress.seven.fredrikst/?p=106",
"status": "publish",
"main_category": "Prevensjon"
}
}
这就是我创建此回复的方式:
$menu = Menu::slug('hovedmeny')->first();
$res = [];
foreach ($menu->nav_items as $item) {
$item->makeHidden($hiddenAttributes)->toArray();
$parent_id = $item->meta->_menu_item_menu_item_parent;
if ($parent_id == '0') {
if ($item->title == '') {
$item = $this->findPost($item);
}
$parentItem = $item;
$res[$parentItem->title] = $parentItem->makeHidden($hiddenAttributes)->toArray();
}
else {
$childItem = $this->findPost($item);
$res[$parentItem->title]['submenus'][] = $childItem->makeHidden($hiddenAttributes)->toArray();
}
}
return $res;
我遇到的问题是 WP 的响应只有 returns parent_id
每个 $item
而没有关于某个项目是否有一些 children 的数据,所以这是 parent 项目的元数据,例如:
#attributes: array:4 [
"meta_id" => 209
"post_id" => 112
"meta_key" => "_menu_item_menu_item_parent"
"meta_value" => "0"
]
这是 child 项的元数据:
#attributes: array:4 [
"meta_id" => 326
"post_id" => 135
"meta_key" => "_menu_item_menu_item_parent"
"meta_value" => "112"
]
我怎样才能使它灵活并启用更深的嵌套,以便我可以在子菜单中包含子菜单?
我试图寻找解决方案 here,因为这与我的问题几乎相同,但无法实施。
在我的数组菜单项中也只有 parent_id
,parent_id
即 0
被视为根元素。另外 parent_id
如果 menu item
是 post
,指向 meta id
,而不是我需要的 post
的 id,所以我需要从 meta->_menu_item_object_id
.
获得额外的信息
更新
我已经设法制作了一个树状结构,但我现在遇到的问题是我不知道如何为 posts
的菜单元素获取 title
。我在前面的示例中通过检查 title
是否为空来做到这一点,然后我将通过 id
:
搜索 post
if ($item->title == '') {
$item = $this->findPost($item);
}
但是,使用新代码,我正在制作树状结构,但我不确定该怎么做,因为我正在将所有内容与 id
,并且菜单元素的 ids
与指向的 post
的 id
不同,所以我无法制作树结构:
private function menuBuilder($menuItems, $parentId = 0)
{
$hiddenAttributes = \Config::get('middleton.wp.menuHiddenAttributes');
$res = [];
foreach ($menuItems as $index => $item) {
$itemParentId = $item->meta->_menu_item_menu_item_parent;
if ($itemParentId == $parentId) {
$children = self::menuBuilder($menuItems, $item->ID);
if ($children) {
$item['submenu'] = $children;
}
$res[$item->ID] = $item->makeHidden($hiddenAttributes)->toArray();
unset($menuItems[$index]);
}
}
return $res;
}
所以,那么我得到的数据是:
{
"112": {
"ID": 112,
"submenu": {
"135": {
"ID": 135,
"title": "",
"slug": "135",
"url": "http://hivnorge.app/?p=135",
"status": "publish",
"main_category": "Hovedmeny"
}
},
"title": "Hjem",
"slug": "hjem",
"url": "http://hivnorge.app/?p=112",
"status": "publish",
"main_category": "Hovedmeny"
},
"136": {
"ID": 136,
"submenu": {
"137": {
"ID": 137,
"submenu": {
"138": {
"ID": 138,
"title": "",
"slug": "138",
"url": "http://hivnorge.app/?p=138",
"status": "publish",
"main_category": "Hovedmeny"
}
},
"title": "",
"slug": "137",
"url": "http://hivnorge.app/?p=137",
"status": "publish",
"main_category": "Hovedmeny"
}
},
"title": "",
"slug": "136",
"url": "http://hivnorge.app/?p=136",
"status": "publish",
"main_category": "Hovedmeny"
},
"139": {
"ID": 139,
"title": "",
"slug": "139",
"url": "http://hivnorge.app/?p=139",
"status": "publish",
"main_category": "Hovedmeny"
}
}
所以你需要写一个递归函数见What is a RECURSIVE Function in PHP?
所以像
function menuBuilder($menuItems){
foreach($menuItems as $key => $item)
{
if(!empty($item->children)){
$output[$key] = menuBuilder($item->children);
}
}
return $output;
}
解决这个问题的一种方法是使用变量别名。如果您注意管理 ID 的查找-table(数组),您可以使用它作为不同的变量插入到分层菜单数组的正确位置(这里是查找中的数组条目 table) 可以引用相同的值。
在下面的示例中对此进行了演示。它还解决了第二个问题(隐含在你的问题中)平面数组未排序(顺序在数据库结果中未定义table),因此子菜单条目可以在结果集中之前 子菜单项所属的菜单项。
对于示例,我创建了一个简单的平面数组:
# some example rows as the flat array
$rows = [
['id' => 3, 'parent_id' => 2, 'name' => 'Subcategory A'],
['id' => 1, 'parent_id' => null, 'name' => 'Home'],
['id' => 2, 'parent_id' => null, 'name' => 'Categories'],
['id' => 4, 'parent_id' => 2, 'name' => 'Subcategory B'],
];
然后要完成的工作有两个主要变量:第一个 $menu
是要创建的层次数组,第二个 $byId
是查找 table:
# initialize the menu structure
$menu = []; # the menu structure
$byId = []; # menu ID-table (temporary)
lookuptable只在菜单建好后才需要,之后就扔掉了。
下一个重要步骤是通过遍历平面数组来创建 $menu
。这是一个更大的 foreach 循环:
# build the menu (hierarchy) from flat $rows traversable
foreach ($rows as $row) {
# map row to local ID variables
$id = $row['id'];
$parentId = $row['parent_id'];
# build the entry
$entry = $row;
# init submenus for the entry
$entry['submenus'] = &$byId[$id]['submenus']; # [1]
# register the entry in the menu structure
if (null === $parentId) {
# special case that an entry has no parent
$menu[] = &$entry;
} else {
# second special case that an entry has a parent
$byId[$parentId]['submenus'][] = &$entry;
}
# register the entry as well in the menu ID-table
$byId[$id] = &$entry;
# unset foreach (loop) entry alias
unset($entry);
}
这是条目从平面数组 ($rows
) 映射到分层 $menu
数组的地方。由于堆栈和查找,不需要递归-table $byId
.
这里的关键点是在将新条目添加到 $menu
结构以及将它们添加到 $byId
时使用变量别名(引用)。这允许使用两个不同的变量名访问内存中的相同值:
# special case that an entry has no parent
$menu[] = &$entry;
...
# register the entry as well in the menu ID-table
$byId[$id] = &$entry;
这是通过 = &
分配完成的,这意味着 $byId[$id]
可以访问 $menu[<< new key >>]
。
如果将其添加到子菜单,则执行相同的操作:
# second special case that an entry has a parent
$byId[$parentId]['submenus'][] = &$entry;
...
# register the entry as well in the menu ID-table
$byId[$id] = &$entry;
这里$byId[$id]
指向$menu...[ << parent id entry in the array >>]['submenus'][ << new key >> ]
.
这解决了始终找到将新条目插入层次结构的正确位置的问题。
为了处理子菜单出现在平面数组之前它所属的菜单条目的情况,为新条目初始化时的子菜单需要从查找 table(在 [1]):
# init submenus for the entry
$entry['submenus'] = &$byId[$id]['submenus']; # [1]
这有点特殊。如果 $byId[$id]['submenus']
尚未设置(例如在第一个循环中),由于引用(&$byId[$id]['submenus']
前面的 &
),它被隐式设置为 null
.如果已设置,则将使用尚未存在的条目中的现有子菜单来初始化该条目的子菜单。
这样做就足以不依赖于 $rows
.
中的任何特定顺序
这就是循环的作用。
剩下的就是清理工作:
# unset ID aliases
unset($byId);
取消设置外观 ID table,因为不再需要它。也就是说,所有的别名都没有设置。
完成示例:
# visualize the menu structure
print_r($menu);
然后给出以下输出:
Array
(
[0] => Array
(
[id] => 1
[parent_id] =>
[name] => Home
[submenus] =>
)
[1] => Array
(
[id] => 2
[parent_id] =>
[name] => Categories
[submenus] => Array
(
[0] => Array
(
[id] => 3
[parent_id] => 2
[name] => Subcategory A
[submenus] =>
)
[1] => Array
(
[id] => 4
[parent_id] => 2
[name] => Subcategory B
[submenus] =>
)
)
)
)
我希望这是可以理解的,并且您能够将其应用到您的具体场景中。您可以将它包装在它自己的函数中(我会建议这样做),我只是为了更好地演示这些部分而将其冗长。
相关问答material:
- Php: Converting a flat array into a tree like structure
- Convert a series of parent-child relationships into a hierarchical tree?
- Build a tree from a flat array in PHP
我正在根据从 WP 数据库获得的响应制作一个嵌套菜单数组。在 corcel package 的帮助下,我从 Laravel 中控制器中的 WP 获取数据,然后制作一个包含菜单数据的数组,该数组现在是一层深。因此,当菜单 link 有子菜单 links 时,数组如下所示:
{
"Hjem": {
"ID": 112,
"title": "Hjem",
"slug": "hjem",
"url": "http://hivnorge.app/?p=112",
"status": "publish",
"main_category": "Hovedmeny",
"submenus": [
{
"ID": 129,
"title": "Lorem ipsum",
"slug": "lorem-ipsum",
"url": "http://hivnorge.app/?p=129",
"status": "publish",
"main_category": "Nyheter"
}
]
},
"Nytt test innlegg": {
"ID": 127,
"title": "Nytt test innlegg",
"slug": "nytt-test-innlegg",
"url": "http://hivnorge.app/?p=127",
"status": "private",
"main_category": "Nyheter",
"submenus": [
{
"ID": 125,
"title": "Test innlegg",
"slug": "test-innlegg",
"url": "http://hivnorge.app/?p=125",
"status": "publish",
"main_category": "Nyheter"
},
{
"ID": 129,
"title": "Lorem ipsum",
"slug": "lorem-ipsum",
"url": "http://hivnorge.app/?p=129",
"status": "publish",
"main_category": "Nyheter"
}
]
},
"Prosjektsamarbeidets verdi": {
"ID": 106,
"title": "Prosjektsamarbeidets verdi",
"slug": "prosjektsamarbeidets-verdi",
"url": "http://hivnorge.no.wordpress.seven.fredrikst/?p=106",
"status": "publish",
"main_category": "Prevensjon"
}
}
这就是我创建此回复的方式:
$menu = Menu::slug('hovedmeny')->first();
$res = [];
foreach ($menu->nav_items as $item) {
$item->makeHidden($hiddenAttributes)->toArray();
$parent_id = $item->meta->_menu_item_menu_item_parent;
if ($parent_id == '0') {
if ($item->title == '') {
$item = $this->findPost($item);
}
$parentItem = $item;
$res[$parentItem->title] = $parentItem->makeHidden($hiddenAttributes)->toArray();
}
else {
$childItem = $this->findPost($item);
$res[$parentItem->title]['submenus'][] = $childItem->makeHidden($hiddenAttributes)->toArray();
}
}
return $res;
我遇到的问题是 WP 的响应只有 returns parent_id
每个 $item
而没有关于某个项目是否有一些 children 的数据,所以这是 parent 项目的元数据,例如:
#attributes: array:4 [
"meta_id" => 209
"post_id" => 112
"meta_key" => "_menu_item_menu_item_parent"
"meta_value" => "0"
]
这是 child 项的元数据:
#attributes: array:4 [
"meta_id" => 326
"post_id" => 135
"meta_key" => "_menu_item_menu_item_parent"
"meta_value" => "112"
]
我怎样才能使它灵活并启用更深的嵌套,以便我可以在子菜单中包含子菜单?
我试图寻找解决方案 here,因为这与我的问题几乎相同,但无法实施。
在我的数组菜单项中也只有 parent_id
,parent_id
即 0
被视为根元素。另外 parent_id
如果 menu item
是 post
,指向 meta id
,而不是我需要的 post
的 id,所以我需要从 meta->_menu_item_object_id
.
更新
我已经设法制作了一个树状结构,但我现在遇到的问题是我不知道如何为 posts
的菜单元素获取 title
。我在前面的示例中通过检查 title
是否为空来做到这一点,然后我将通过 id
:
post
if ($item->title == '') {
$item = $this->findPost($item);
}
但是,使用新代码,我正在制作树状结构,但我不确定该怎么做,因为我正在将所有内容与 id
,并且菜单元素的 ids
与指向的 post
的 id
不同,所以我无法制作树结构:
private function menuBuilder($menuItems, $parentId = 0)
{
$hiddenAttributes = \Config::get('middleton.wp.menuHiddenAttributes');
$res = [];
foreach ($menuItems as $index => $item) {
$itemParentId = $item->meta->_menu_item_menu_item_parent;
if ($itemParentId == $parentId) {
$children = self::menuBuilder($menuItems, $item->ID);
if ($children) {
$item['submenu'] = $children;
}
$res[$item->ID] = $item->makeHidden($hiddenAttributes)->toArray();
unset($menuItems[$index]);
}
}
return $res;
}
所以,那么我得到的数据是:
{
"112": {
"ID": 112,
"submenu": {
"135": {
"ID": 135,
"title": "",
"slug": "135",
"url": "http://hivnorge.app/?p=135",
"status": "publish",
"main_category": "Hovedmeny"
}
},
"title": "Hjem",
"slug": "hjem",
"url": "http://hivnorge.app/?p=112",
"status": "publish",
"main_category": "Hovedmeny"
},
"136": {
"ID": 136,
"submenu": {
"137": {
"ID": 137,
"submenu": {
"138": {
"ID": 138,
"title": "",
"slug": "138",
"url": "http://hivnorge.app/?p=138",
"status": "publish",
"main_category": "Hovedmeny"
}
},
"title": "",
"slug": "137",
"url": "http://hivnorge.app/?p=137",
"status": "publish",
"main_category": "Hovedmeny"
}
},
"title": "",
"slug": "136",
"url": "http://hivnorge.app/?p=136",
"status": "publish",
"main_category": "Hovedmeny"
},
"139": {
"ID": 139,
"title": "",
"slug": "139",
"url": "http://hivnorge.app/?p=139",
"status": "publish",
"main_category": "Hovedmeny"
}
}
所以你需要写一个递归函数见What is a RECURSIVE Function in PHP?
所以像
function menuBuilder($menuItems){
foreach($menuItems as $key => $item)
{
if(!empty($item->children)){
$output[$key] = menuBuilder($item->children);
}
}
return $output;
}
解决这个问题的一种方法是使用变量别名。如果您注意管理 ID 的查找-table(数组),您可以使用它作为不同的变量插入到分层菜单数组的正确位置(这里是查找中的数组条目 table) 可以引用相同的值。
在下面的示例中对此进行了演示。它还解决了第二个问题(隐含在你的问题中)平面数组未排序(顺序在数据库结果中未定义table),因此子菜单条目可以在结果集中之前 子菜单项所属的菜单项。
对于示例,我创建了一个简单的平面数组:
# some example rows as the flat array
$rows = [
['id' => 3, 'parent_id' => 2, 'name' => 'Subcategory A'],
['id' => 1, 'parent_id' => null, 'name' => 'Home'],
['id' => 2, 'parent_id' => null, 'name' => 'Categories'],
['id' => 4, 'parent_id' => 2, 'name' => 'Subcategory B'],
];
然后要完成的工作有两个主要变量:第一个 $menu
是要创建的层次数组,第二个 $byId
是查找 table:
# initialize the menu structure
$menu = []; # the menu structure
$byId = []; # menu ID-table (temporary)
lookuptable只在菜单建好后才需要,之后就扔掉了。
下一个重要步骤是通过遍历平面数组来创建 $menu
。这是一个更大的 foreach 循环:
# build the menu (hierarchy) from flat $rows traversable
foreach ($rows as $row) {
# map row to local ID variables
$id = $row['id'];
$parentId = $row['parent_id'];
# build the entry
$entry = $row;
# init submenus for the entry
$entry['submenus'] = &$byId[$id]['submenus']; # [1]
# register the entry in the menu structure
if (null === $parentId) {
# special case that an entry has no parent
$menu[] = &$entry;
} else {
# second special case that an entry has a parent
$byId[$parentId]['submenus'][] = &$entry;
}
# register the entry as well in the menu ID-table
$byId[$id] = &$entry;
# unset foreach (loop) entry alias
unset($entry);
}
这是条目从平面数组 ($rows
) 映射到分层 $menu
数组的地方。由于堆栈和查找,不需要递归-table $byId
.
这里的关键点是在将新条目添加到 $menu
结构以及将它们添加到 $byId
时使用变量别名(引用)。这允许使用两个不同的变量名访问内存中的相同值:
# special case that an entry has no parent
$menu[] = &$entry;
...
# register the entry as well in the menu ID-table
$byId[$id] = &$entry;
这是通过 = &
分配完成的,这意味着 $byId[$id]
可以访问 $menu[<< new key >>]
。
如果将其添加到子菜单,则执行相同的操作:
# second special case that an entry has a parent
$byId[$parentId]['submenus'][] = &$entry;
...
# register the entry as well in the menu ID-table
$byId[$id] = &$entry;
这里$byId[$id]
指向$menu...[ << parent id entry in the array >>]['submenus'][ << new key >> ]
.
这解决了始终找到将新条目插入层次结构的正确位置的问题。
为了处理子菜单出现在平面数组之前它所属的菜单条目的情况,为新条目初始化时的子菜单需要从查找 table(在 [1]):
# init submenus for the entry
$entry['submenus'] = &$byId[$id]['submenus']; # [1]
这有点特殊。如果 $byId[$id]['submenus']
尚未设置(例如在第一个循环中),由于引用(&$byId[$id]['submenus']
前面的 &
),它被隐式设置为 null
.如果已设置,则将使用尚未存在的条目中的现有子菜单来初始化该条目的子菜单。
这样做就足以不依赖于 $rows
.
这就是循环的作用。
剩下的就是清理工作:
# unset ID aliases
unset($byId);
取消设置外观 ID table,因为不再需要它。也就是说,所有的别名都没有设置。
完成示例:
# visualize the menu structure
print_r($menu);
然后给出以下输出:
Array
(
[0] => Array
(
[id] => 1
[parent_id] =>
[name] => Home
[submenus] =>
)
[1] => Array
(
[id] => 2
[parent_id] =>
[name] => Categories
[submenus] => Array
(
[0] => Array
(
[id] => 3
[parent_id] => 2
[name] => Subcategory A
[submenus] =>
)
[1] => Array
(
[id] => 4
[parent_id] => 2
[name] => Subcategory B
[submenus] =>
)
)
)
)
我希望这是可以理解的,并且您能够将其应用到您的具体场景中。您可以将它包装在它自己的函数中(我会建议这样做),我只是为了更好地演示这些部分而将其冗长。
相关问答material:
- Php: Converting a flat array into a tree like structure
- Convert a series of parent-child relationships into a hierarchical tree?
- Build a tree from a flat array in PHP