软件开发过程中我们经常会遇到各种各样多级的数据结构需求。刚开始与产品沟通的最多只支持3级。后期根据需求的迭代3级变成多级再变成无限级。有时候产品为了减少麻烦产品初级就会定义支持无限的分类结构。分类的产品需求非常简单可能就一句话支持无限级的分类数据结构。但分类的代码开发繁琐麻烦有时候还有点复杂。
今天为大家介绍两种无限级分类设计方案。大家可以根据自身的业务需求合理使用解决方案。
第一种方案:父ID方式
1、表结构设计
CREATE TABLE IF NOT EXISTS `classify`(
`class_id` INT UNSIGNED AUTO_INCREMENT COMMENT '自增分类id',
`class_name` VARCHAR(128) NOT NULL COMMENT '分类名称',
`type` SMALLINT(1) NOT NULL DEFAULT 1 COMMENT '分类类型',
`parent_id` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '分类父级id',
`status` SMALLINT(1) UNSIGNED NOT NULL DEFAULT 1 COMMENT '1 正常 2 删除',
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '更新时间',
`ext_data` VARCHAR(1024) NOT NULL DEFAULT '[]' COMMENT '扩展信息json',
PRIMARY KEY ( `class_id` )
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT '综合业务分类表';
#批量插入测试数据
INSERT INTO classify(`class_id`, `class_name`, `parent_id`, `status`) values(1, 'a', 0, 1),(2,'a1',1,1),(3,'b',0,1),(4,'a11',2,1),(5,'a12',2,1)
2、代码开发
#查询所有分类数据(执行db一次)
$conn = new mysqli('127.0.0.1', 'root', '', 'test');
if ($conn->connect_error) {
die("连接失败: " . $conn->connect_error);
}
$sql = "SELECT class_id, class_name, parent_id FROM classify WHERE `type` = 1";
$result = $conn->query($sql);
$arrList = array();
while($row = $result->fetch_assoc()) {
$arrList[] = $row;
}
#递归函数组合分类数据结构
function build_classify_tree($arrList,$intClassId) {
$arrTree = array();
foreach($arrList as $arrItem) {
if($arrItem['parent_id'] == $intClassId) {
$arrItem['child'] = build_classify_tree($arrList, $arrItem['class_id']);
$arrTree[] = $arrItem;
}
}
return $arrTree;
}
$arrTree = build_classify_tree($arrList, 0);
$conn->close();
3、执行结果
array(2) {
[0]=>
array(4) {
["class_id"]=>
string(1) "1"
["class_name"]=>
string(1) "a"
["parent_id"]=>
string(1) "0"
["child"]=>
array(1) {
[0]=>
array(4) {
["class_id"]=>
string(1) "2"
["class_name"]=>
string(2) "a1"
["parent_id"]=>
string(1) "1"
["child"]=>
array(2) {
[0]=>
array(4) {
["class_id"]=>
string(1) "4"
["class_name"]=>
string(3) "a11"
["parent_id"]=>
string(1) "2"
["child"]=>
array(0) {
}
}
[1]=>
array(4) {
["class_id"]=>
string(1) "5"
["class_name"]=>
string(3) "a12"
["parent_id"]=>
string(1) "2"
["child"]=>
array(0) {
}
}
}
}
}
}
[1]=>
array(4) {
["class_id"]=>
string(1) "3"
["class_name"]=>
string(1) "b"
["parent_id"]=>
string(1) "0"
["child"]=>
array(0) {
}
}
}
第二种方案:父路径方式
1、表结构设计
CREATE TABLE IF NOT EXISTS `category`(
`class_id` INT UNSIGNED AUTO_INCREMENT COMMENT '自增分类id',
`class_name` VARCHAR(128) NOT NULL COMMENT '分类名称',
`type` SMALLINT(1) NOT NULL DEFAULT 1 COMMENT '分类类型',
`parent_path` VARCHAR(2048) NOT NULL DEFAULT '' COMMENT '分类父级路径',
`status` SMALLINT(1) UNSIGNED NOT NULL DEFAULT 1 COMMENT '1 正常 2 删除',
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '更新时间',
`ext_data` VARCHAR(1024) NOT NULL DEFAULT '[]' COMMENT '扩展信息json',
PRIMARY KEY ( `class_id` )
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
#批量插入测试数据
INSERT INTO category(`class_id`, `class_name`, `parent_path`, `status`) values(1, 'a', '/', 1),(2,'a1','/a/',1),(3,'b','/',1),(4,'a11','/a/a1/',1),(5,'a12', '/a/a1/', 1)
2、代码设计
$conn = new mysqli('127.0.0.1', 'root', '', 'test');
if ($conn->connect_error) {
die("连接失败: " . $conn->connect_error);
}
$sql = "SELECT class_id, class_name, parent_path FROM category WHERE `type` = 1";
$result = $conn->query($sql);
$arrList = array();
while($row = $result->fetch_assoc()) {
$arrList[] = $row;
}
function build_classify_tree($arrList,$strParentPath) {
$arrTree = array();
foreach($arrList as $arrItem) {
if($arrItem['parent_path'] == $strParentPath) {
$strChildParentPath = sprintf('%s%s', $strParentPath, $arrItem['class_name'] . '/');
$arrItem['child'] = build_classify_tree($arrList, $strChildParentPath);
$arrTree[] = $arrItem;
}
}
return $arrTree;
}
$arrTree = build_classify_tree($arrList, '/');
var_dump($arrTree);
$conn->close();
3、执行结果
array(2) {
[0]=>
array(4) {
["class_id"]=>
string(1) "1"
["class_name"]=>
string(1) "a"
["parent_path"]=>
string(1) "/"
["child"]=>
array(1) {
[0]=>
array(4) {
["class_id"]=>
string(1) "2"
["class_name"]=>
string(2) "a1"
["parent_path"]=>
string(3) "/a/"
["child"]=>
array(2) {
[0]=>
array(4) {
["class_id"]=>
string(1) "4"
["class_name"]=>
string(3) "a11"
["parent_path"]=>
string(6) "/a/a1/"
["child"]=>
NULL
}
[1]=>
array(4) {
["class_id"]=>
string(1) "5"
["class_name"]=>
string(3) "a12"
["parent_path"]=>
string(6) "/a/a1/"
["child"]=>
NULL
}
}
}
}
}
[1]=>
array(4) {
["class_id"]=>
string(1) "3"
["class_name"]=>
string(1) "b"
["parent_path"]=>
string(1) "/"
["child"]=>
NULL
}
}
大家倾向于哪种方案呢?我个人比较喜欢第二种方案因为分类结构在表里一眼就可以看出来。
,感谢大家的评论、点赞、分享、关注。。。