当前位置: 首页 > 后端技术 > Java

基于levelStr

时间:2023-04-01 21:45:57 Java

无限分类的无限等级分类的实现,支持快速获取所有子等级量或记录https://github.com/SmallFores...设计表是无线等级分类的一种思路.常规无限分类数据表的设计一般是这样的:idpidnameis_deleteCREATETABLE`category_normal`(`id`int(11)unsignedNOTNULLAUTO_INCREMENT,`pid`int(11)DEFAULT'0'COMMENT'parent'sid',`name`varchar(255)DEFAULT''COMMENT'类别名称',`is_delete`tinyint(1)DEFAULT'0'COMMENT'被删除:0未删除1被删除',PRIMARYKEY(`id`))ENGINE=InnoDB默认字符集=utf8mb4;这样的设计有一个很难实现的需求:获取指定类别的所有下属类别的个数。递归当然可以实现这样的要求,但是效率很低。于是优化数据表optimizationCREATETABLE`category_optimization`(`id`int(11)unsignedNOTNULLAUTO_INCREMENT,`pid`int(11)DEFAULT'0'COMMENT'parent'sid',`name`varchar(255)DEFAULT''COMMENT'类别名称',`is_delete`tinyint(1)DEFAULT'0'COMMENT'是否删除:0未删除1删除',`level_str`varchar(2000)DEFAULT''COMMENT'级别字符串',PRIMARYKEY(`id`))ENGINE=InnoDBDEFAULTCHARSET=utf8mb4;modeldesigninsertGetId(["pid"=>$pid,"name"=>$name,"is_delete"=>0,"level_str"=>"",]);$this->where('id',$id)->update(['level_str'=>$this->createLevelStr($id)]);返回(整数)$id;}/***定义生成level_str,按照pid的规则,按照pid规则倒序拼接L个字符串*@paramint$id当前记录的ID*/privatefunctioncreateLevelStr(int$id):字符串{//'|'非常重要eg:L10|$str='L'.$身份证。'|';//获取pid$pid=$this->getPID($id);//获取pid的LevelStr,与当前str拼接一起返回return$str.$this->getLevelStr($pid);}/***获取父ID*@paramint$id*@returnint*/privatefunctiongetPID(int$id):int{return(int)$this->where('id',$id)->where('is_delete',0)->value('pid');}/***getlevel_strstring*@paramint$id*@returnstring*/privatefunctiongetLevelStr(int$id):string{//有一条父ID等于0的记录,此时只要直接返回空字符串,不查询数据库if($id==0)return"";返回(字符串)$this->where('id',$id)->where('is_delete',0)->值('level_str');}/***删除该级别id的所有孩子,包括孙、曾孙,不包括自己*@paramint$id*/publicfunctiondeleteSons(int$id){$this->where('is_delete',0)->where('id','<>',$id)->where('level_str','like','%L'.$id.'|%')->update(["is_delete"=>1]);/***parent修改自己的pid,不会影响所有children的pid*但会影响所有children的level_str*实现思路是替换*@paramstring$oldStrstrbeforechange*@paramstring$newStrstrafterchange*/privatefunctionmodifySonLevelStr(string$oldStr,string$newStr){//更新platform_car_brand_partsset`level_str`=replace(`level_str`,'L4|L1|','L4|')where`level_str`像'%L4|L1|%'和`is_delete`=0;$sql="update`{$this->table}`set`level_str`=replace(`level_str`,'{$oldStr}','{$newStr}')where`level_str`like'%{$oldStr}%'”。“和‘is_delete’=0”;数据库::执行($sql);}/***修改父ID**@paraminteger$id级别ID*@paraminteger$pid父修改ID*@returnvoid*/publicfunctionchangePid(int$id,int$pid){//添加限制条件:你不能将自己的孩子设置为自己的父母$parents_ids=$this->getParentsIds($pid);if(in_array($id,$parents_ids)){thrownewException("禁止将父级Cut直接设置为子级");}//step1更新父ID$this->where('id',$id)->update(['pid'=>$pid]);//step2得到原来的level_str$oldStr=$this->getLevelStr($id);//step3切换parentid后,需要获取一个新的level_str$newStr=$this->createLevelStr($id);if($oldStr!=$newStr){//步骤4更新level_str$this->where('id',$id)->update(['level_str'=>$newStr]);//step5修改所有孩子的level_str$this->modifySonLevelStr($oldStr,$newStr);}}/***获取所有父母*@paramint$id*/privatefunctiongetParentsIds(int$id){$level_str=$this->where('id',$id)->value('level_str');preg_match_all('/\d+/',$level_str,$m);如果(计数($m[0])==0){返回[];}$ids=array_reverse($m[0]);//移除最后一个元素array_pop($ids);如果(!empty($ids)){foreach($idsas&$id){$id=intval($id);}}返回$ids;}/***获取该级别ID的所有孩子的个数,包括孙子、曾孙等*@paramint$id*@returnint*/publicfunctiongetSonCount(int$id):int{//原理:level_str的格式是L4L3L1,所以可以使用likestatisticsreturn$this->where([['level_str','like','%L'.$id.'|%']])->where('is_delete',0)->count('id')-1;}/***获取紧邻关卡ID的孩子个数,就下一个孩子,不包括孙子、曾孙等。*@paramint$id*@returnint*/publicfunctiongetNextSonCount(int$id):int{return$this->where([['pid','=',$id],['is_delete','=',0]])->count('id');}}部署测试使用docker部署。定义网络名称:fenlei_jll。要使用的MySQL容器名称是mysql01。项目中要使用mysql01,需要将mysql01加入到fenlei_jll的网络中。dockernetworkconnectfenlei_jllmysql01然后使用dockerinspectmysql01检查它。容器启动后,进入容器执行命令php/usr/share/nginx/html/index.phpbug分类数据量巨大时,获取子数量会很慢。publicfunctioncreateLevelStr(int$id):string{//'|'非常重要$str='L'。$身份证。'|';//获取pid$pid=$this->getPID($id);//获取pid的LevelStr,调整与当前str拼接返回的顺序return$this->getLevelStr($pid)。$海峡;}获取子级别的方式也更新了publicfunctiongetSonCount(int$id):int{//SELECTcount(id)from`level`wherelevel_strlike'L1|%';//使用左索引,like可以去索引,否则GG//原理:level_str的格式为L1|L2|L3|所以你可以使用类似的统计数据Quantity$pStr=$this->getLevelStr($id);返回$this->where([['level_str','like',$pStr.'%']])->where('is_delete',0)->count('id')-1;}修改后的图像