轉自:https://blog.csdn.net/belen_xue/article/details/59121554php
在各類欄目以及分類設置中,無限分類常常會被用到,而無限分類在進行排序的時候必然要用到遞歸,這裏進行一次詳細的分析解讀。mysql
首先咱們先了解一下遞歸函數:sql
遞歸函數在語言學習的時候會單獨拿出來學習,由於它很是經常使用,本質上來說遞歸函數就是調用本身的函數。數組
舉個例子:函數
<?php function test(){ static $a=0; if($a<10){ $a++; test(); } echo $a."<br/>"; } test();
test函數裏面又再調用了自身,這就是俗稱的遞歸函數!遞歸函數都有條件設置,否則的會無限循環下去,這樣會致使程序奔潰。學習
因此通常來說我總結遞歸函數有兩個特色:fetch
一個是記錄條件值,記錄的條件值必須保證不會再下一次調用時丟失。spa
test函數裏$a即是記錄條件值,它是依靠使用static關鍵字來保證記錄每次增長的數值不會再下一次調用test()函數而丟失,由於函數中static修飾的變量僅僅在第一次初始化,並保留變量值。因此只要保證這一點,不光static,其餘的方式也能夠達到目的,例如global還有&修飾符。.net
另外一個是條件檢查。test裏面對$a大小的限制就是該條件的檢查過程。code
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,'|--');
這樣排序的結果以下:
整個遞歸函數的執行狀況大概是什麼樣的,咱們接上面說過的要畫的實例圖,看圖後就很清楚這個原理了,這個圖只畫了部分,不過足夠理解這個過程了: