Lef*_*eff 6 php arrays tree recursion submenu
我从WP数据库得到的响应中创建了一个嵌套的菜单数组.我在Corcel包的帮助下从Laravel控制器中的WP获取数据,然后使用菜单数据创建一个数组,现在这个数据已经达到了一个级别.因此,当菜单链接具有子菜单链接时,该数组如下所示:
{
"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"
}
}
Run Code Online (Sandbox Code Playgroud)
这就是我创建此响应的方式:
$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;
Run Code Online (Sandbox Code Playgroud)
我遇到的问题是WP的响应只返回parent_id
每个,$item
而且没有关于项是否有子项的数据,所以这是父项的元数据,例如:
#attributes: array:4 [
"meta_id" => 209
"post_id" => 112
"meta_key" => "_menu_item_menu_item_parent"
"meta_value" => "0"
]
Run Code Online (Sandbox Code Playgroud)
这是子项的元数据:
#attributes: array:4 [
"meta_id" => 326
"post_id" => 135
"meta_key" => "_menu_item_menu_item_parent"
"meta_value" => "112"
]
Run Code Online (Sandbox Code Playgroud)
如何使这种灵活性和更深层次的嵌套,以便我可以在子菜单中有子菜单?
我试图在这里寻找解决方案,因为这与我的问题几乎相同,但无法实现.在我的数组菜单中,项目也只有parent_id
,并且parent_id
它0
被视为根元素.另外,parent_id
如果menu item
是post
,指向meta id
,而不是ID的post
,我需要,所以我需要从additionaly meta->_menu_item_object_id
.
UPDATE
我已经设法制作了一个类似树的结构,但我现在遇到的问题是我不知道如何获得title
菜单元素posts
.我在上一个例子中通过检查是否title
为空来执行此操作然后我将post
通过id
以下方式搜索:
if ($item->title == '') {
$item = $this->findPost($item);
}
Run Code Online (Sandbox Code Playgroud)
但是,使用新代码,我在制作类似树的结构,我不知道该怎么做,因为那时我无法创建树结构,因为我将所有内容与菜单元素id
和ids
菜单元素进行比较不同于id
的post
是指向,这样的话我不能让树结构:
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;
}
Run Code Online (Sandbox Code Playgroud)
那么,我得到的数据是:
{
"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"
}
}
Run Code Online (Sandbox Code Playgroud)
解决此问题的一种方法是使用变量别名。如果您注意管理 ID 的查找表(数组),您可以利用它插入分层菜单数组的正确位置,因为不同的变量(此处为查找表中的数组条目)可以引用相同的值。
在以下示例中对此进行了演示。它还解决了第二个问题(隐含在您的问题中),即平面数组未排序(数据库结果表中的顺序未定义),因此子菜单条目可以位于结果集中子菜单条目所属的菜单条目之前。
对于这个例子,我创建了一个简单的平面数组:
# 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'],
];
Run Code Online (Sandbox Code Playgroud)
然后,要完成的工作有两个主要变量:第一个$menu
是要创建的分层数组,第二个$byId
是查找表:
# initialize the menu structure
$menu = []; # the menu structure
$byId = []; # menu ID-table (temporary)
Run Code Online (Sandbox Code Playgroud)
查找表仅在菜单构建时才需要,之后将被丢弃。
$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);
}
Run Code Online (Sandbox Code Playgroud)
这是条目从平面数组 ( $rows
) 映射到分层$menu
数组的地方。由于堆栈和查找表,不需要递归$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;
Run Code Online (Sandbox Code Playgroud)
它是通过= &
分配完成的,这意味着$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;
Run Code Online (Sandbox Code Playgroud)
这里$byId[$id]
指向$menu...[ << parent id entry in the array >>]['submenus'][ << new key >> ]
.
这解决了始终找到在分层结构中插入新条目的正确位置的问题。
为了处理子菜单出现在其所属菜单条目之前的平面数组中的情况,在为新条目初始化时需要从查找表中取出子菜单(在[1]处):
# init submenus for the entry
$entry['submenus'] = &$byId[$id]['submenus']; # [1]
Run Code Online (Sandbox Code Playgroud)
这是一个有点特殊的情况。如果$byId[$id]['submenus']
尚未设置(例如在第一个循环中),则null
由于引用(&
前面的&$byId[$id]['submenus']
),它会被隐式设置为 。如果已设置,则将使用尚不存在的条目中的现有子菜单来初始化该条目的子菜单。
这样做就足以不依赖于 中的任何特定顺序$rows
。
这就是循环的作用。
剩下的就是清理工作:
# unset ID aliases
unset($byId);
Run Code Online (Sandbox Code Playgroud)
它会取消设置外观 ID 表,因为不再需要它。也就是说,所有别名均未设置。
要完成该示例:
# visualize the menu structure
print_r($menu);
Run Code Online (Sandbox Code Playgroud)
然后给出以下输出:
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] =>
)
)
)
)
Run Code Online (Sandbox Code Playgroud)
我希望这是可以理解的,并且您能够将其应用到您的具体场景中。您可以将其包装在它自己的函数中(我建议这样做),我只是为了让示例保持冗长以更好地演示各个部分。
相关问答材料: