Php無限層級並顯示層級數

今天在處理遞歸無限層級菜單時,遇到一個稍微燒腦的問題,如何顯示當前節點所在的層級數。
廢話很少說,咱們先看個直觀的無限層級: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的全部節點層級關係呢html

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

顯然不對,何況菜單展現並不友好,pid=3的父節點至少要顯示在頂層吧。層級數暫且無論,咱們先解決如何顯示頂層pid=3的樹形結構:
pid=3對應的節點是:二級菜單a。這個能夠直接根據數據id查詢出來,此處僅作演示哈。調整下代碼:vue

$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集合:完整代碼以下:npm

<?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,好比:json

$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);

 看起來沒毛病,博友們若是有好的辦法獲取任意一個節點所處的層級數,歡迎拍磚留言哈。數組

相關文章
相關標籤/搜索