php 无限分类递归及其排序详细解读
转自:https://blog.csdn.net/belen_xue/article/details/59121554
在各种栏目以及分类设置中,无限分类经常会被用到,而无限分类在进行排序的时候必然要用到递归,这里进行一次详细的分析解读。
首先我们先了解一下递归函数:
递归函数在语言学习的时候会单独拿出来学习,因为它非常常用,本质上来讲递归函数就是调用自己的函数。
举个例子:
<?php function test(){ static $a=0; if($a<10){ $a++; test(); } echo $a."<br/>"; } test();
test函数里面又再调用了自身,这就是俗称的递归函数!递归函数都有条件设置,不然的会无限循环下去,这样会导致程序奔溃。
所以一般来讲我总结递归函数有两个特点:
一个是记录条件值,记录的条件值必须保证不会再下一次调用时丢失。
test函数里$a便是记录条件值,它是依靠使用static关键字来保证记录每次增加的数值不会再下一次调用test()函数而丢失,因为函数中static修饰的变量仅仅在第一次初始化,并保留变量值。所以只要保证这一点,不光static,其他的方式也可以达到目的,例如global还有&修饰符。
另一个是条件检查。test里面对$a大小的限制就是该条件的检查过程。
test函数中if($a<10)就是这个条件检查的过程,它限制了test()函数对自身的调用,这样就可以防止无限调用导致程序奔溃。
函数a内部调用另外的函数b,如果b函数没有完成,那么a函数就会一直等待下去,直到b函数完成,才会回到a函数继续执行。递归的过程中利用了这个特性,正是这个能帮助我们对无限分类进行排序,用上面test()递归函数说明:
上面test()打印出来的结果是:
10,10,10,10,10,10,10,10,10,10,10
可能和很多人想的不一样,大概有许多人会觉得不应该是0,1,2,3,4,5,6,7,8,9,10这样的结果吗?
这是因为函数test调用过程中,只要$a<10,就会调用自身的test(),每次调用的test()都没有到达echo处,也就是每次调用的test函数并没有完结,直到$a递增到了10,才第一次echo $a,这个时候$a已经是10了,因此第一个10实际是递归了11次后的$a,第十一次递归的test由于不符合<10的条件该函数完结,这个时候才开始回到递归第十次test函数里执行echo,这个时候由于$a是静态变量,值已经是10了,因此echo出的结果是10,下面依次回到之前的test函数完成前面未完成的echo步骤,因此echo出11个10,最后一个10实际是第一次执行test函数的echo结果。
无限分类的排序完成也是用的这个原理,递归排序函数不断的通过parentid等于上一级id的子类来匹配该类别的子类别,只要找到第一个子类,就用找到的这个子类的id去找下一级的子类,直到没有更下级的子类的时候,才返回上一级接着继续找,找到后又开始寻找该子类下一级子类,直到没有为止才返回,这个过程不断循环。可能用文字不太能理解,下面的实例中我会画出图例,请先往后看。
我们先来看无限分类的数据er图:
例如一个裙子的类目,它有父类别女装,女装又属于衣服的类目,假定裙子的id为3,女装为2,衣服为1,那么裙子的parentid就是直接的上级类目裙子的id,因此parentid=2,而child是裙子所在的树形结构上全部的祖先元素id,那么child应该是1,2,3,因此裙子的深度deep为3,这里我假设的是用,隔开,也可以用其他的符号隔开,title不用说就是裙子。
具体我们来看这个树形结构:
从树形结构分类还可以继续延伸下去,上一级的id是下一级的parentid,就是通过id和parent这两个列来实现基本的无限分类的,再进行无限分类的递归排序的时候也是依靠这两个字段的关系。
下面是一个无限分类的表结构以及数据的例子:
为了更清楚下面画了树形图:
上一代的类别的id就是自身的parentid,最高一级的parentid则为0,这点非常重要,是无限分类的排序的依据。
我们所要的无限分类排序效果应该是各栏目下的子栏目都放到该栏目下方,用id进行排序,我们要的效果就是下面这样,为了方便查看我用了tab以及|——来间隔区分栏目之间的关系:
衣服
|——男装
|——休闲上衣
|——短袖
|——长袖
|——休闲裤
|——女装
|——女装上衣
|——女装下装
|——牛仔裤
|——裙子
由于类别可以无限延伸下去,所以这里明显我们需要使用递归函数进行排序分类。
获取数据所有结果,并按id排序
$mysqli=new mysqli('localhost','root','root','test')or die("连接失败"); $mysqli->set_charset("utf8"); $re=$mysqli->query("select * from col order by id asc"); $result=$re->fetch_all(MYSQLI_ASSOC);
递归排序函数
function recursion($result,$parentid=0){ /*记录排序后的类别数组*/ static $list=array(); foreach ($result as $k => $v){ if($v['parentid']==$parentid){ /*将该类别的数据放入list中*/ $list[]=$v; recursion($result,$v['id']); } } return $list; }
这个时候我们就能得到按类别排序好的数组了,但是如果要有相应的格式,例如上面的|——,就需要对函数进行改进。
function recursion($result,$parentid=0,$format="|--"){ /*记录排序后的类别数组*/ static $list=array(); foreach ($result as $k => $v){ if($v['parentid']==$parentid){ if($parentid!=0){ $v['title']=$format.$v['title']; } /*将该类别的数据放入list中*/ $list[]=$v; recursion($result,$v['id']," ".$format); } } return $list; } $list=recursion($result,0,'|--');
这样排序的结果如下:
整个递归函数的执行情况大概是什么样的,我们接上面说过的要画的实例图,看图后就很清楚这个原理了,这个图只画了部分,不过足够理解这个过程了: