在开发网站后台或者前端导航栏时,多级菜单是非常常见的需求,通常菜单数据会存储在数据库中,每条记录包含自身的id和父级菜单的parent_id,这类层级结构的数据最适合用递归函数来处理。下面我们就一步步讲解如何用PHP递归函数生成动态菜单。

一、准备菜单数据
首先我们需要准备一组模拟的菜单数据,实际开发中这些数据一般是从数据库查询得到的,这里我们直接定义一个数组来模拟:
<?php
// 模拟从数据库查询得到的菜单数据,每条数据包含id、父级id、菜单名称和链接
$menuList = [
['id' => 1, 'parent_id' => 0, 'name' => '首页', 'url' => '/index'],
['id' => 2, 'parent_id' => 0, 'name' => '产品中心', 'url' => '/product'],
['id' => 3, 'parent_id' => 2, 'name' => '手机', 'url' => '/product/phone'],
['id' => 4, 'parent_id' => 2, 'name' => '电脑', 'url' => '/product/computer'],
['id' => 5, 'parent_id' => 3, 'name' => '智能手机', 'url' => '/product/phone/smart'],
['id' => 6, 'parent_id' => 0, 'name' => '新闻资讯', 'url' => '/news'],
['id' => 7, 'parent_id' => 6, 'name' => '行业新闻', 'url' => '/news/industry'],
['id' => 8, 'parent_id' => 6, 'name' => '公司动态', 'url' => '/news/company'],
];
?>二、编写递归函数处理菜单
递归函数的核心思路是:先找到所有顶级菜单(parent_id为0的项),然后对每个顶级菜单,递归查找它的子菜单,直到没有子菜单为止。下面是具体的递归函数实现:
<?php
/**
* 递归生成菜单树
* @param array $menuList 原始菜单数组
* @param int $parentId 当前查找的父级id,默认从0开始查顶级菜单
* @return array 处理后的菜单树数组
*/
function buildMenuTree($menuList, $parentId = 0) {
$tree = [];
// 遍历原始菜单数组,找出所有父级id等于当前parentId的项
foreach ($menuList as $menu) {
if ($menu['parent_id'] == $parentId) {
// 递归查找当前菜单的子菜单,把子菜单赋值给当前菜单的children键
$children = buildMenuTree($menuList, $menu['id']);
if (!empty($children)) {
$menu['children'] = $children;
}
$tree[] = $menu;
}
}
return $tree;
}
// 调用函数生成菜单树
$menuTree = buildMenuTree($menuList);
// 可以打印查看生成的菜单树结构
// print_r($menuTree);
?>三、渲染菜单为HTML
生成菜单树之后,我们还需要把树形结构的数据渲染成对应的HTML代码,这里同样可以用递归的方式来渲染,根据菜单是否有子菜单来决定是否生成下一级
- 列表:
<?php
/**
* 递归渲染菜单为HTML
* @param array $menuTree 菜单树数组
* @return string 渲染后的HTML字符串
*/
function renderMenuHtml($menuTree) {
$html = '<ul class="menu-list">';
foreach ($menuTree as $menu) {
$html .= '<li>';
$html .= '<a href="' . $menu['url'] . '">' . $menu['name'] . '</a>';
// 如果当前菜单有子菜单,递归渲染子菜单
if (isset($menu['children']) && !empty($menu['children'])) {
$html .= renderMenuHtml($menu['children']);
}
$html .= '</li>';
}
$html .= '</ul>';
return $html;
}
// 输出渲染后的菜单HTML
echo renderMenuHtml($menuTree);
?>四、注意事项
使用递归函数生成菜单时需要注意几个问题:
- 递归深度问题:如果菜单层级非常深,可能会导致递归栈溢出,一般菜单层级不会超过5级,这种情况可以忽略,如果层级很深可以考虑改用迭代方式处理。
- 数据完整性:原始菜单数据中如果parent_id指向了不存在的id,递归函数不会报错,但可能会生成空的子菜单,建议提前校验数据的合法性。
- 性能问题:如果菜单数据量非常大,递归函数遍历多次原始数组会有性能损耗,可以提前把原始数组按parent_id分组,减少遍历次数。
五、优化后的递归函数
针对上面的性能问题,我们可以先把原始菜单按parent_id分组,再递归生成菜单树,减少数组遍历的次数:
<?php
/**
* 优化后的菜单树生成函数,先按parent_id分组原始数据
* @param array $menuList 原始菜单数组
* @return array 处理后的菜单树数组
*/
function buildMenuTreeOptimized($menuList) {
// 按parent_id分组
$grouped = [];
foreach ($menuList as $menu) {
$grouped[$menu['parent_id']][] = $menu;
}
// 递归生成树
$buildTree = function($parentId) use (&$buildTree, $grouped) {
$tree = [];
if (isset($grouped[$parentId])) {
foreach ($grouped[$parentId] as $menu) {
$children = $buildTree($menu['id']);
if (!empty($children)) {
$menu['children'] = $children;
}
$tree[] = $menu;
}
}
return $tree;
};
return $buildTree(0);
}
// 调用优化后的函数
$optimizedMenuTree = buildMenuTreeOptimized($menuList);
echo renderMenuHtml($optimizedMenuTree);
?>以上就是用PHP递归函数生成动态菜单的完整过程,从数据准备到递归处理再到最终渲染,都给出了可直接运行的示例代码,开发者可以根据自己的实际需求调整菜单的样式和逻辑。