Php无限层级并显示层级数

今天在处理递归无限层级菜单时,遇到一个稍微烧脑的问题,如何显示当前节点所在的层级数。
废话不多说,我们先看个直观的无限层级:

<?php
// 这里的arr是直接从数据库取出的,仅作为测试数据
$arr = array(
    array(‘id‘ => 1, ‘name‘ => ‘一级菜单a‘, ‘pid‘ => 0),// pid 父级id
    array(‘id‘ => 2, ‘name‘ => ‘一级菜单b‘, ‘pid‘ => 0),
    array(‘id‘ => 3, ‘name‘ => ‘二级菜单a‘, ‘pid‘ => 1),
    array(‘id‘ => 4, ‘name‘ => ‘二级菜单b‘, ‘pid‘ => 1),
    array(‘id‘ => 5, ‘name‘ => ‘二级菜单c‘, ‘pid‘ => 2),
    array(‘id‘ => 6, ‘name‘ => ‘二级菜单d‘, ‘pid‘ => 2),
    array(‘id‘ => 7, ‘name‘ => ‘三级菜单a‘, ‘pid‘ => 3),
    array(‘id‘ => 8, ‘name‘ => ‘三级菜单b‘, ‘pid‘ => 3),
    array(‘id‘ => 9, ‘name‘ => ‘四级菜单a‘, ‘pid‘ => 8),
);

/** 获取所有子节点
 * @param $data 所有节点数组
 * @param $id   $pid 父级节点id
 * @param $level  层级
 * @return array
 */
function getTree($data, $pid, $level = 0)
{
    $list = array();
    foreach ($data as $k => $v) {
        if ($v[‘pid‘] == $pid) {
            $v[‘level‘] = $level;
            $v[‘name‘] = $v[‘name‘].‘(‘.($level+1).‘级)‘; // 这里可以加个层级次数
            $v[‘children‘] = getTree($data, $v[‘id‘], $level + 1);
            if ($v[‘children‘] == null){
                unset($v[‘children‘]);
            }
            $list[] = $v;
        }
    }
    return $list;
}

$menu = getTree($arr, 0, 0);

$json = json_encode($menu);
?>
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>

    <script src="https://cdn.jsdelivr.net/npm/vue"></script>

    <style>
        *{
            box-sizing: border-box;
            margin: 0;padding: 0;
        }
        *:before,*:after{
            box-sizing: border-box;
        }
        ul,
        li {
            list-style: none;
        }

        .l_tree_container {
            width: 100%;
            height: 100%;
            box-shadow: 0 0 3px #ccc;
            margin: 13px;
            position: relative;
        }

        .l_tree {
            width: calc(100% - 44px);
            height: 100%;
            padding-left: 42px;
        }
        .l_tree_branch {
            width: 100%;
            height: 100%;
            display: block;
            padding: 13px;
            position: relative;
        }

        .l_tree_branch .l_tree_children_btn {
            width: 19px;
            height: 19px;
            background-color: #23b1f0;
            font-size: 14px;
            text-align: center;
            color: #ffffff;
            outline: none;
            border: 0;
            cursor: pointer;
        }

        ul.l_tree:before {
            content: ‘‘;
            border-left: 1px dashed #999999;
            height: calc(100%);
            position: absolute;
            left: 10px;
            top: 0px;
        }

        .l_tree .l_tree_branch:last-child::before {
            content: ‘‘;
            width: 3px;
            height: calc(100% - 24px);
            display: block;
            background-color: #ffffff;
            position: absolute;
            bottom: 0;
            left: -34px;
        }

        .l_tree,
        .l_tree_branch {
            position: relative;
        }

        .l_tree_branch::after {
            content: ‘‘;
            width: 40px;
            height: 0;
            border-bottom: 1px dashed #000;
            position: absolute;
            right: calc(100% - 9px);
            top: 22px;
        }

        .l_tree_container>.l_tree::before,
        .l_tree_container>.l_tree>.l_tree_branch::after {
            display: none;
        }
    </style>
</head>

<body>

<div id="demo">
    <div class="l_tree_container">
        <ew-tree :model="testdata"></ew-tree>
    </div>
</div>

<script>
    // 树组件
    Vue.component(‘ew-tree‘, {
        template: `
        <ul class="l_tree">
            <li class="l_tree_branch" v-for="item in model" :key="item.id">
                <div class="l_tree_click">
                    <button type="button" class="l_tree_children_btn" v-if="item.children"  @click="toggle(item)">{{ !item.show ? ‘-‘ : ‘+‘ }}</button>
                    <span class="l_folder">{{ item.name }}</span>
                </div>
                <ew-tree v-show="!item.show" v-if="item.children" :model="item.children"></ew-tree>
            </li>
        </ul>`,
        props: {
            model: {}
        },
        methods: {
            toggle: function (item) {
                var idx = this.model.indexOf(item)
                Vue.set(this.model[idx], ‘show‘, !item.show)
            }
        }
    });
    new Vue({
        el: "#demo",
        data() {
            return {
                testdata: <?php echo $json?>
            }
        }
    })
</script>

</body>
</html>

我们看到所有节点层级数没问题,那么我如何查看节点中pid=3的所有节点层级关系呢

$menu = getTree($arr, 3, 0);

显然不对,况且菜单展示并不友好,pid=3的父节点至少要显示在顶层吧。层级数暂且不管,我们先解决如何显示顶层pid=3的树形结构:
pid=3对应的节点是:二级菜单a。这个可以直接根据数据id查询出来,此处仅做演示哈。调整下代码:

$menu = getTree($arr, 3, 0);
$menu = array([‘name‘ => ‘二级菜单a‘,‘children‘=> $menu]);
$json = json_encode($menu);

OK,完美,同样要显示所有pid=1的节点层级关系,一样,pid=1对应的节点是 一级菜单a

$menu = getTree($arr, 1, 0);
$menu = array([‘name‘ => ‘一级菜单a‘,‘children‘=> $menu]);
$json = json_encode($menu);

好,回到 刚才的话题,如何正确的显示每个节点所在的层级数呢? 这里我也思考了很久,也没找到快捷的方法。
最终我还是递归的查询本节点所有的父节点id集合:完整代码如下:

<?php
// 这里的arr是直接从数据库取出的,仅作为测试数据
$arr = array(
    array(‘id‘ => 1, ‘name‘ => ‘一级菜单a‘, ‘pid‘ => 0),// pid 父级id
    array(‘id‘ => 2, ‘name‘ => ‘一级菜单b‘, ‘pid‘ => 0),
    array(‘id‘ => 3, ‘name‘ => ‘二级菜单a‘, ‘pid‘ => 1),
    array(‘id‘ => 4, ‘name‘ => ‘二级菜单b‘, ‘pid‘ => 1),
    array(‘id‘ => 5, ‘name‘ => ‘二级菜单c‘, ‘pid‘ => 2),
    array(‘id‘ => 6, ‘name‘ => ‘二级菜单d‘, ‘pid‘ => 2),
    array(‘id‘ => 7, ‘name‘ => ‘三级菜单a‘, ‘pid‘ => 3),
    array(‘id‘ => 8, ‘name‘ => ‘三级菜单b‘, ‘pid‘ => 3),
    array(‘id‘ => 9, ‘name‘ => ‘四级菜单a‘, ‘pid‘ => 8),
);

/** 获取所有子节点
 * @param $data 所有节点数组
 * @param $id $pid 父级节点id
 * @param $level  层级
 * @return array
 */
function getTree($data, $pid, $level = 0)
{
    $list = array();
    foreach ($data as $k => $v) {
        if ($v[‘pid‘] == $pid) {
            $v[‘level‘] = $level;
            $v[‘name‘] = $v[‘name‘] . ‘(‘ . ($level + 1) . ‘级)‘; // 这里可以加个层级次数
            $v[‘children‘] = getTree($data, $v[‘id‘], $level + 1);
            if ($v[‘children‘] == null) {
                unset($v[‘children‘]);
            }
            $list[] = $v;
        }
    }
    return $list;
}

/** 根据子节点获取父节点id
 * @param $data 所有节点数组
 * @param $id   id  主键id
 * @return array
 */
function getParentid($data, $id)
{
    $arr = array();
    foreach ($data as $v) {
        if ($v[‘id‘] == $id) {
            $arr[] = $v;
            //$arr[$v[‘id‘]]=$v[‘name‘];
            $arr = array_merge(getParentid($data, $v[‘pid‘]), $arr);
        }
    }
    return $arr;

}

$id = 8 ; // 对应的节点是: 三级菜单b 对应的pid 是 3
$pid = 3; // 对应的节点是  二级菜单a

$toparr = getParentid($arr, $id); // 节点为8的所有父节点 id: 1 3  8 这里包含了自身,注意剔除

$level = count($toparr); // 节点所在的层级数
$menu = getTree($arr, $pid, $level-1);
if($pid)
    $menu = array([‘name‘ => ‘二级菜单a‘ . ‘(‘ . ($level-1) . ‘级)‘, ‘children‘ => $menu]);

$json = json_encode($menu);
?>
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>

    <script src="https://cdn.jsdelivr.net/npm/vue"></script>

    <style>
        * {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }

        *:before, *:after {
            box-sizing: border-box;
        }

        ul,
        li {
            list-style: none;
        }

        .l_tree_container {
            width: 100%;
            height: 100%;
            box-shadow: 0 0 3px #ccc;
            margin: 13px;
            position: relative;
        }

        .l_tree {
            width: calc(100% - 44px);
            height: 100%;
            padding-left: 42px;
        }

        .l_tree_branch {
            width: 100%;
            height: 100%;
            display: block;
            padding: 13px;
            position: relative;
        }

        .l_tree_branch .l_tree_children_btn {
            width: 19px;
            height: 19px;
            background-color: #23b1f0;
            font-size: 14px;
            text-align: center;
            color: #ffffff;
            outline: none;
            border: 0;
            cursor: pointer;
        }

        ul.l_tree:before {
            content: ‘‘;
            border-left: 1px dashed #999999;
            height: calc(100%);
            position: absolute;
            left: 10px;
            top: 0px;
        }

        .l_tree .l_tree_branch:last-child::before {
            content: ‘‘;
            width: 3px;
            height: calc(100% - 24px);
            display: block;
            background-color: #ffffff;
            position: absolute;
            bottom: 0;
            left: -34px;
        }

        .l_tree,
        .l_tree_branch {
            position: relative;
        }

        .l_tree_branch::after {
            content: ‘‘;
            width: 40px;
            height: 0;
            border-bottom: 1px dashed #000;
            position: absolute;
            right: calc(100% - 9px);
            top: 22px;
        }

        .l_tree_container > .l_tree::before,
        .l_tree_container > .l_tree > .l_tree_branch::after {
            display: none;
        }
    </style>
</head>

<body>

<div id="demo">
    <div class="l_tree_container">
        <ew-tree :model="testdata"></ew-tree>
    </div>
</div>

<script>
    // 树组件
    Vue.component(‘ew-tree‘, {
        template: `
        <ul class="l_tree">
            <li class="l_tree_branch" v-for="item in model" :key="item.id">
                <div class="l_tree_click">
                    <button type="button" class="l_tree_children_btn" v-if="item.children"  @click="toggle(item)">{{ !item.show ? ‘-‘ : ‘+‘ }}</button>
                    <span class="l_folder">{{ item.name }}</span>
                </div>
                <ew-tree v-show="!item.show" v-if="item.children" :model="item.children"></ew-tree>
            </li>
        </ul>`,
        props: {
            model: {}
        },
        methods: {
            toggle: function (item) {
                var idx = this.model.indexOf(item)
                Vue.set(this.model[idx], ‘show‘, !item.show)
            }
        }
    });
    new Vue({
        el: "#demo",
        data() {
            return {
                testdata: <?php echo $json?>
            }
        }
    })
</script>

</body>
</html>

同样要显示所有节点:直接把pid赋值0,比如:

$id = 2 ; // 对应的节点是: 一级菜单b 对应的pid 是 0
$pid = 0; // 对应的节点是 0 为 一级节点  显示所有节点,上面的$id用不到

$toparr = getParentid($arr, $id); //

$level = count($toparr); // 节点所在的层级数
$menu = getTree($arr, $pid, $level-1);
if($pid)
    $menu = array([‘name‘ => ‘二级菜单a‘ . ‘(‘ . ($level-1) . ‘级)‘, ‘children‘ => $menu]);

$json = json_encode($menu);

看起来没毛病,博友们如果有好的办法获取任意一个节点所处的层级数,欢迎拍砖留言哈。

原文地址:https://www.cnblogs.com/phpper/p/10821203.html

时间: 2024-08-20 21:49:22

Php无限层级并显示层级数的相关文章

无限层级且乱序的树形结构数据的整理,利用HashMap减少遍历次数

我们在展示一个机构树的时候,常常会遇到这样的一个问题,查询数据的时候,是从下往上查的,但展示数据的时候,又要从下往上展示. 这时候就要把查询到的数据进行整理从而得到我们想要的结构. 举个例子. ID PARENT_ID SOME_ATTRIBUTE_ID 2001 0   6292 6120 57010 6120 6115   6121 6115   6156 6121 56874 6115 2001   这是根据需求查询出的sql数据,但是它是无序的,所以很让人头疼,不知如何去处理,示意图是这

小程序无限层级路由方案

小程序无限层级路由方案 小程序原生页面存在层级限制,超过一定层数就会无法打开新页面.一开始这个限制为不超过5层,目前是不超过10层. 这个限制对于体量较大的小程序来说,挺难受的.特别是只能打开5层那会儿,业务流程很容易一不小心就超了,比如:首页-搜索结果页-商品详情页-聊天页-下单页-地址选择页-...:更有访问回路防不胜防,比如:商品详情页-查看更多页-商品详情页-查看更多页-....商品详情页-聊天页-个人主页-商品详情页-聊天页-个人主页-商品详情页-....诸如此类.即使后来放宽至了10

JavaEE使用三层架构(显示层、业务逻辑层、数据访问层)实现数据的增删改查

实例: 1.功能描述 实现一个简易新闻发布系统,包括查看.添加.修改和删除新闻等基本功能 2.具体要求 (1) 创建数据库 newssystem,创建表 news,要求如下: (2) 程序运行时,显示'发布新闻'页面(如图 1),输入相关内容,单击'提交'按钮,将新闻内容添加到数据库 (3) 单击图 1 中的'查看'按钮,显示'查看新闻'页面(如图 2),增加'修改'和'删除'链接 (4) 单击图 2 中的'update'链接,显示'修改新闻'页面(如图 3),修改后单击'修改'按钮确认,单击'

Android上的MVP:如何组织显示层的内容

MVP(Model View Presenter)模式是著名的MVC(Model View Controller)模式的一个演化版本,目前它在Android应用开发中越来越重要了,大家也都在讨论关于MVP的理论,只是结构化的资料非常少.这就是我写这篇博客的原因,我想鼓励大家多参与讨论,然后把MVP模式运用在项目开发中. 什么是MVP? MVP模式可以分离显示层和逻辑层,所以功能接口如何工作与功能的展示可以实现分离,MVP模式理想化地可以实现同一份逻辑代码搭配不同的显示界面.首先要澄清就是MVP不

MVC框架显示层——Velocity技术

转自:  http://blog.csdn.net/fasttalk/article/details/398059 Velocity,名称字面翻译为:速度.速率.迅速,用在Web开发里,用过的人可能不多,大都基本知道和在使用Struts,到底Velocity和Struts(Taglib和Tiles)是如何联系?在技术上Velocity要比Struts Struts(Taglib和Tiles)先进,单从技术上考虑可以考虑Velocity:而Struts用的非常普遍,这样的人才也很多,所以对于公司来

WebAPI增加Area以支持无限层级同名Controller

原文:WebAPI增加Area以支持无限层级同名Controller 微软的WebAPI默认实现逻辑 默认实现中不支持同名Controller,否则在访问时会报HttpError,在网上找到了各种路由自实现,如 给ASP.net Web API的Controller分类 搭建MVC及WebAPI项目框架时碰到的问题集合 在上述地址的帮助下,根据需求,重新编写了AreaHttpControllerSelector,路由原理与上述地址大同小异,均是通过路由匹配拼接FullName,然后匹配最接近的A

struts2_8_接受显示层传递的参数

struts.xml文件的配置: <struts> <package name="package" namespace="/test" extends="struts-default"> <action name="emp*" class="struts.employeeAction" method="{1}"> <result name=&qu

实现鼠标移到某个对象,在旁边显示层。

当鼠标移到某个地方的时候,在旁边飘出一个DIV,离开则消失. 是使用了某位大大封装的一个函数,稍微改了下. 原文地址: 当鼠标移动上去显示一个层,在这个对象的旁边,并且可以给这个层添加值 效果如下: 函数代码,放到一个JS文件里: [javascript] view plaincopyprint? /** * 鼠标移上去显示层 * @param divId 显示的层ID * @returns */ $.fn.myHoverTip = function(divId) { var div = $("

系统管理模块_部门管理_改进_抽取添加与修改JSP页面中的公共代码_在显示层抽取BaseAction_合并Service层与Dao层

系统管理模块_部门管理_改进1:抽取添加与修改JSP页面中的公共代码 commons.jspf <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@ taglib prefix="s" uri="/struts-tags" %> <meta http-equiv="Conten