背景
- 最近复习算法,
在此对无限级分类的实现方法稍作整理,当然也是参考了道友的经验,
目测适合实际的项目应用,
当然,也有不少公司的笔试题还会涉及到呢,有何问题,欢迎各位道友指摘 …
操作环境:
Win10
使用语言:PHP
使用框架:ThinkPHP 3.2.3
前期准备
- ①. 首先,实现无限级分类的方式有:
> 1. 以父 ID设计,运用递归实现的方式
> 2. 以全路径实现的无限分类方式
- ②. 其次,数据表设计思路
- 对应于上述的两种实现方式,那么在数据表设计时也可以有两种方式,参考所给的数据表截图
> 1. 如果采用 父ID方式,
字段只需使用 "id,pid,cate_name" 的主要三个即可
> 2. 如果采用 全路径方式,
字段只需使用 "id,cate_name,path "的主要三个即可,注意:full_path 其实只作参考即可
代码实现
- 此处,讲解几个核心方法,
完整代码可根据后面的附录进行下载参考,
请注意此处提及的核心处理代码都在文件ZmModel.class.php
中
①. 父 ID 方式
- 核心处理代码如下,注意参数备注信息,便于正确的调用
/**
* 数据库设计 递归方式 获取无限极分类数据 由上到下进行获取
* @param int $pid 父级ID,默认为根级分类 0
* @param int $sel_id 所选中的分类ID,多用于前端 selected 标识
* @param array $result 数组整合
* @param int $spac 空格间隔,便于前端缩进显示分类所属级别
* @return array
*/
public function deepCatesForDown($pid = 0,$sel_id = 0,&$result = [],$spac = 0){
//空格数目
$spac += 2;
//从数据表中获取 父级ID为所需 pid 的全部数据
$cateList = $this->db_cate
->where("pid = $pid")
->select();
//TODO 进行遍历处理
foreach ($cateList as $key => $value){
//判断 selected 属性
if ($sel_id == $value['id']){
$selectedStr = "selected";
}else{
$selectedStr = "";
}
$cateList[$key]['cate_name'] = str_repeat(' ',$spac)
.'|--'.$cateList[$key]['cate_name'] ;
$cateList[$key]['selected'] = $selectedStr;
$result[] = $cateList[$key];
//TODO 此处进行了递归操作
$this->deepCatesForDown($value['id'],$sel_id,$result,$spac);
}
return $result;
}
- 控制器调用参考:
$zmModel = new ZmModel();
$cateListDown = $zmModel->deepCatesForDown(0,6);
$this->assign('cateListDown',$cateListDown);
$this->display();
- 前端 Html 数据显示参考:
<h4>递归方式获取 无限级分类数据</h4>
<select>
<volist name="cateListDown" id="vo">
<option {$vo.selected}>{$vo.cate_name}</option>
</volist>
</select>
②. 全路径实现方式
- 对应参考上面的介绍方式,核心处理代码如下
/**
* 全路径方式 获取无限极分类数据 由上到下进行获取
* @return array
*/
public function deepCatesFullPathForDown(){
//注意排序方式 自动按要求进行排列
$cateList = $this->db_cate
->field("id,cate_name,path,concat(path,',',id) full_path")
->order('full_path asc')
->select();
$result = [];
//遍历数据 方法同上
foreach ($cateList as $key => $value){
$deep = explode(',',trim($value['full_path'],','));
$cateList[$key]['cate_name'] = str_repeat(' ',count($deep))
."|--".$cateList[$key]['cate_name'];
$result[] = $cateList[$key];
}
return $result;
}
- 调用及前端显示代码参考同上
实现效果
- 上述的参考代码,只做了下拉框的实现参考,对于全链接方式的实现可直接参考源代码
附录
源代码下载 >>>
补充信息 (2021-02-27)
-
毕竟本篇文章已历史悠远
在鄙人的开发过程中,感觉代码方式发生了很大改变,在此做点补充信息! -
以分类数据表设计为例:
第一种,代码实现
- 这种方式,是先获取所有的分类数据,然后经过递归方法处理
最终得到一个多维数组
缺点:如果数据量庞大,操作会很慢(普遍递归都是这个缺点!)
/**
* 递归获取,全部树状分类数据
* @param array $arr 分类数组
* @param int $pid 父ID
* @return array
*/
public function tree($arr = [], $pid = 0){
$list = [];
foreach ($arr as $key=> $val){
if ($val['parent_id'] == $pid){
//把这个节点从数组中移除,减少后续递归消耗
unset($arr[$key]);
//开始递归,查找父ID为该节点ID的节点,级别则为原级别+1
$child = $this->tree($arr,$val['cat_id']);
if ($child){
$val['child'] = $child;
}
$list[] = $val;
}
}
return $list;
}
/**
* 测试接口
*/
public function test(){
$map = [['status', '=', 1]];
$res = Db::name('xcategorys')
->field('cat_id,cat_name,parent_id')
->where($map)
->order(["list_order"=>"asc","cat_id"=>'asc'])
->select();
$result = $this->tree($res,0);
var_dump($result);
}
- 最终获取的分类数组数据展示如下:
第二种,代码实现
-
对比上面的方式
是在递归方法中,获取数据库数据实现上来讲比较好理解,但是不适合集成通用的方法,毕竟每个表字段都会有所出入的!
/**
* 集成 待选商品分类数据
* @param int $level
* @param int $parent_id
* @return array
*/
public function getCmsToSelCategoryList($level = 1,$parent_id = 0)
{
$map = [['level', '=', $level],['status', '=', 1]];
$map[] = ['parent_id', '=', $parent_id];
$res = $this
->field('cat_id,cat_name')
->where($map)
->order(["list_order"=>"asc","cat_id"=>'asc'])
->select();
foreach ($res as $key => $value){
$childRes = $this->getCmsToSelCategoryList(intval($level+1),intval($value['cat_id']));
$res[$key]['children'] = $childRes;
}
return isset($res) ? $res->toArray() : [];
}