无限级分类的简单实现

引子

作为菜鸟的我面试过程中总是会被虐的体无完肤,即使知道是怎么一回事,但由于没有彻底掌握住,还是在关键时刻无法及时运用。所以多总结应该是我现在时常要做的事。
遇到的这个机试题就是关于无限级分类的知识。无限级分类在项目中运用是比较平常的,逻辑也不会太难理解:一个父级下面包含多个子级。
题目是这样的:点击下拉框选择,输入名称,添加,到对应的层级中。然后还要可以删除。
实现大概效果图如下:


图1

图2

实现

1、数据库

数据库

添加的逻辑:当某行数据的pid等于另一行数据中的id时,如id为54的广州市,其pid=53,即对应的是广东省,所以广州市是在广东省下面的。以此类推。

删除的逻辑:删除某个父类,其下所有子类都需删除。

2、代码

为了效果稍微好点,采取了jq\ajax,并用了mvc框架。

Controller:
    public function index()
    {
        $data = DB::name('user')->order('id asc')->select();
        //调用树
        $li = new PersonModel;
        $tree = $li->getTree($data);
        $html = $li->html($tree);
        $this->assign('tree',$tree);
        $this->assign('data',$data);
        $this->assign('html',$html);
        return $this->fetch();
    }

    public function add()
    {
        $data = $_POST;
        $pid = $_POST['pid'];
        if($data) {
            if($pid == 0) {
                $result = DB::name('user')->insert(['name'=>$data['name'],'pid'=>0]);//根目录
            } else {
                $insert = DB::name('user')->insert(['name'=>$data['name'],'pid'=>$pid]);//子目录
            }
            
            $status = 1;
            $message = '成功';
            return ['status' => $status, 'message' => $message];
        } else {
            $status = 0;
            $message = '失败';
            return ['status' => $status, 'message' => $message];
        }
    }
    public function del()
    {
        $data = $_POST;
        $id = $_POST['id'];
        //获取子集ID
        $getIds = new PersonModel;
        $son_ids = $getIds->sonIds($id);
        $id .= $son_ids; //将删除的父级加上
        $ids = explode(',', $id);
        if($ids) {
            foreach ($ids as $key => $value) {
                DB::name('user')->where("id='$value'")->delete();
            }
            $status = 1;
            $message = '成功';
            return ['status'=> $status, 'message' => $message];
        } else {
            $status = 0;
            $message = '失败';
            return ['status'=> $status,'message'=>$message];
        }
    }

Model:

//无极限分类---引用传参
    public function li($array)
    {
        $items = array();
        foreach ($array as $key => $value) {
            $items[$value['id']] = $value;
        }
        $tree = array();
        foreach ($items as $key => $value) {
            if(isset($items[$value['pid']])) {
                $items[$value['pid']]['son'][] = &$items[$key];
            } else {
                $tree[] = &$items[$key];
            }
        }
        return $tree;
    }

//无极限分类--递归
    public function getTree($array, $pid =0, $level = 0){

        //声明静态数组,避免递归调用时,多次声明导致数组覆盖
        static $list = [];
        foreach ($array as $key => $value){
            //第一次遍历,找到父节点为根节点的节点 也就是pid=0的节点
            if ($value['pid'] == $pid){
                //父节点为根节点的节点,级别为0,也就是第一级
                $value['level'] = $level;
                //把数组放到list中
                $list[] = $value;
                //把这个节点从数组中移除,减少后续递归消耗
                unset($array[$key]);
                //开始递归,查找父ID为该节点ID的节点,级别则为原级别+1
                $this->getTree($array, $value['id'], $level+1);

            }
        }
        return $list;
    }
//输出效果
    public function html($tree)
    {
        foreach ($tree as $key => $value) {
            echo str_repeat('|----', $value['level']), $value['name'].'<br />';
        }
    }

//获取删除中的子id集
    public function sonIds($id)
    {
        $ids = '';
        $data = DB::name('user')->where("pid='$id'")->select();
        if($data) {
                foreach ($data as $key => $value) {
                    $ids .= ','.$value['id'];
                    $ids .= $this->sonIds($value['id']);
                }
        }
        return $ids;
    }

其中有两种方法实现:递归和引用

前端:

<!DOCTYPE html>
<html>
<meta charset="utf-8">
<head>
    <title>无限级分类</title>
</head>
<body>
<div>
{$html}
        <form action="" method="post" id="formadd">
            选择:
            <div>
                <select name="selected" id="sel">
                    <option value="0">根</option>
                {foreach $data as $val}
                    <option value="{$val['id']}">{$val['name']}</option>
                {/foreach}
                </select>
            
            添加分类:
            
                <input type="text" name="add" id="content"><button id="add">添加</button>
            
            </div>
        </form>
    <div>
        选择:
        <select name="delselect" id="del">
        {foreach $data as $va}
            <option value="{$va['id']}">{$va['name']}</option>
        {/foreach}
        </select>
        <button id="delete">删除</button>
    </div>
</div>
</body>
<script type="text/javascript" src="../../mvc/public/js/jquery-3.3.1.min.js"></script>
<script type="text/javascript">

$("#add").on('click',function(){
    var select = $("#sel").val();
    var name = $("#content").val();
    console.log(select);
    $.ajax({
        type: 'post',
        async:false,
        url:"{:url('index/index/add')}",
        data:{name:name,pid:select},
        datatype:'json',
        success:function (data) {
            //console.log(data.status);
            if(data.status == 1){
                //alert('成功');
                widows.location.reload()
            } else {
                alert('失败');
            }
        },
        error:function (data) {
            alert('直接失败');
        }
    })
});
$("#delete").on('click',function(){
    var select = $("#del").val();
    confirm('删除要谨慎,确认要删除吗?');
    console.log(select);
    $.ajax({
        type: 'post',
        async:false,
        url:"{:url('index/index/del')}",
        data:{id:select},
        datatype:'json',
        success:function (data) {
            console.log(data);
            if(data.status == 1){
                //alert('成功');
                location.reload()
            } else {
                alert('失败');
            }
        },
        error:function (data) {
            //alert('直接失败');
            console.log(data);
        }
    })
})
</script>
</html>

3、代码分析

添加功能:

添加的两种方法:递归和引用,其原理都一样,建立一个空数组,然后就是父亲找儿子的过程,将对应的儿子放在父亲下面。再将其放入空数组中,形成树结构。添加的功能其实就是一个ajax的实现,主要是显示到html中的查询代码处理。逻辑顺序是:选择对应的父类,获取其id,name添加到数据库,model里的方法处理要显示出来的内容。最后返回给页面。

& 引用,将指针指向原数据,后面再循环的时候添加改变数据则是在原数据上改变,所以,可改变原数组。

.= 点等于的意思是拼接等号左右两边的值,如 a = 1,b = 2,则 a .= ','.b 为1,2

删除功能

通过点击选择父类id,查找出其下所有子id。方法就是通过上面说的点等于符号,在遍历中将其所有的id封装成一个字符串返回。再拼接上父级id,最后使用explode函数将字符串变成数组。

总结

php中无限级分类实现的常用两种方法——递归与引用。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容