php 遞歸無(wú)限分類(lèi)

參考
因?yàn)檫f歸的效率相對(duì)來(lái)說(shuō)比較低,所以很少使用成肘,尤其是在查詢(xún)數(shù)據(jù)庫(kù)的時(shí)候?qū)?shù)據(jù)庫(kù)的長(zhǎng)時(shí)間占用很不合理卖局。

但是遞歸遍歷無(wú)限分類(lèi)列表還是相當(dāng)重要的,而且還可以對(duì)數(shù)據(jù)進(jìn)行格式化處理双霍,如果嫌棄效率問(wèn)題砚偶,本人給你一個(gè)好建議就是緩存。比如當(dāng)用戶(hù)登錄的時(shí)候判斷完用戶(hù)的權(quán)限后獲取用戶(hù)可以操作的菜單洒闸,然后遞歸格式化菜單分類(lèi)列表染坯,然后緩存起來(lái)搞旭,這樣用戶(hù)之后的操作都不會(huì)涉及到遞歸獲取菜單占婉,效率可以大大提升。

個(gè)人理解 蔓纠。其實(shí)遞歸分類(lèi)的本質(zhì)是
1.把一個(gè)表里面的所有數(shù)據(jù)都取出來(lái)
2-1.如果fid ==val['fid']
2-2.把數(shù)據(jù)遍歷后鸣个,重新組合羞反。把名字根據(jù)分類(lèi)級(jí)別,添加n倍“---”
2-3.添加完之后囤萤,還有遞歸調(diào)用本函數(shù)

以下是在laravel中使用遞歸遍歷菜單的方法:

  • 1.在項(xiàng)目的Common目錄中的function.php中添加以下函數(shù):
/**
 *
 * @param array $data 結(jié)果集 (整個(gè)表的結(jié)果)
 * @param int $fid 父類(lèi)ID
 * @param array $result 結(jié)果數(shù)據(jù)
 * @param int $deep 分類(lèi)級(jí)數(shù)
 * @return array
 */
function getList($data, $fid = 0, &$result = array(), $deep = 0)
{
    $deep += 1;
    foreach($data as $key => $val)   //遍歷之后昼窗,此處的$key是鍵(其實(shí)是數(shù)組的序列號(hào)),$val是一條數(shù)據(jù)結(jié)果
    {
    $val = $this->objectToArray($val);   //注意涛舍,此處$val是對(duì)象澄惊,需要轉(zhuǎn)換成數(shù)組
        if($fid == $val['fid'])
        {
            $result[$key]['id'] = $val['id'];
            $result[$key]['name'] = "|".str_repeat("--", $deep).$val['name'];
            $result[$key]['fid'] = $val['fid'];
            $this->getList($data, $val['id'], $result, $deep);
        }
    }
    return $result;
}
  • 2.在需要的地方直接getList($data)就可以,比如在Controller中:
public function test()
    {
        $data = \DB::table('test')->get();

         $newClass = $this->getList($data);
        return view('welcome',compact('newClass'));

    }
數(shù)據(jù)表
dd($data)結(jié)果
dd($newClass,查看getList()函數(shù)的執(zhí)行順序)
echo $key."\n"結(jié)果
print_r($val)."\n";結(jié)果
$newClass = $this->getList($data); dd($newClass); 報(bào)錯(cuò)
dd($newClass);結(jié)果
getList()函數(shù)中富雅,如果$result不加傳指符$,dd($newClass)結(jié)果)
  • 3.前臺(tái)直接遍歷輸出就可以了掸驱。
<select name="class">
   @foreach($newClass as  $v)
        <option value="{{ $v['id'] }}">{{$v['name']}}</option>
   @endforeach
</select>
效果圖
  • 數(shù)據(jù)結(jié)構(gòu)
DROP TABLE IF EXISTS `class`;
CREATE TABLE `class` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `fid` int(10) unsigned NOT NULL DEFAULT '0',
  `name` varchar(50) NOT NULL,
  `status` tinyint(3) unsigned NOT NULL DEFAULT '1',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

補(bǔ)充

    public function test()
{
    $data = \DB::table('test')->get();

     $newClass = $this->getList($data);
        dd($newClass);

}
這個(gè)執(zhí)行會(huì)報(bào)錯(cuò),原因是遍歷$data后的$val值是對(duì)象没佑。所以我們需要一個(gè)對(duì)象轉(zhuǎn)數(shù)組的執(zhí)行

寫(xiě)的有點(diǎn)亂毕贼,主要是為了展示效果圖。下面把源碼重新整理下

// TestController.php里代碼
class TestController extends Controller
{

    public function test()
    {
        $data = \DB::table('test')->get();

         $newClass = $this->getList($data);
        return view('welcome',compact('newClass'));

    }
    /**
     *
     * @param array $data 結(jié)果集 (整個(gè)表的結(jié)果)
     * @param int $fid 父類(lèi)ID
     * @param array $result 結(jié)果數(shù)據(jù)
     * @param int $deep 分類(lèi)級(jí)數(shù)
     * @return array
     */
    public function getList($data, $fid = 0, &$result = array(), $deep = 0)
    {
        $deep += 1;
        foreach($data as $key => $val)
        {
            $val = $this->objectToArray($val);
            if($fid == $val['fid'])
            {
                $result[$key]['id'] = $val['id'];
                $result[$key]['name'] = "|".str_repeat("--", $deep).$val['name'];
                $result[$key]['fid'] = $val['fid'];
                $this->getList($data, $val['id'], $result, $deep);
            }
        }
        return $result;
    }
    /*
     * 數(shù)組轉(zhuǎn)對(duì)象
     */
    public function objectToArray($e)
    {
        $e = (array)$e;
        foreach ($e as $k => $v) {
            if (gettype($v) == 'resource') return;
            if (gettype($v) == 'object' || gettype($v) == 'array')
                $e[$k] = (array)$this->objectToArray($v);
        }
        return $e;
    }
}


//welcome.php
<select name="class">
   @foreach($newClass as  $v)
        <option value="{{ $v['id'] }}">{{$v['name']}}</option>
   @endforeach
</select>

還有一種方法:不使用傳指符蛤奢,而是把接收的result數(shù)組定義成靜態(tài)方法鬼癣。靜態(tài)方法能夠保存原來(lái)的數(shù)據(jù)陶贼,但是如果不使用靜態(tài)方法,result=array();相當(dāng)于每次都把$result定義成數(shù)組待秃,并賦值為空



   public function getList($data, $fid = 0,  $deep = 0)
    {
        static $result=array();
        $deep += 1;
        foreach($data as $key => $val)
        {
            $val = $this->objectToArray($val);
            if($fid == $val['fid'])
            {
                $result[$key]['id'] = $val['id'];
                $result[$key]['name']= "|".str_repeat("--", $deep).$val['name'];
                $result[$key]['fid'] = $val['fid'];
                $this->getList($data, $val['id'],  $deep);
            }
        }
        return $result;
    }

思考

那么拜秧,為什么我們一定要與定義$result=[]呢,這是因?yàn)槲覀兊暮瘮?shù)里面章郁,不僅有if枉氮,還有else。
也就是說(shuō)當(dāng)if不存在時(shí)驱犹,我們執(zhí)行的代碼就是
foreach($data as $key => $val)
        {
            $val = $this->objectToArray($val);
           
        }
        return $result;

這個(gè)時(shí)候嘲恍,我們根本沒(méi)有給$result定義,也沒(méi)有給它賦值雄驹,當(dāng)然就會(huì)報(bào)錯(cuò)
如果想要解決這個(gè)問(wèn)題佃牛,我們只能做個(gè)測(cè)試,來(lái)一個(gè)else,然后給$result賦值

但是這樣還是不能拼接我們獲取的值医舆,我只是說(shuō)我們解決這種情況的處理俘侠。
但是放倒全局來(lái)說(shuō),所以我們還是用static或者傳指符吧

$contry = [
    ['id'=>1,'fid'=>0,'name'=>'國(guó)內(nèi)'],
    ['id'=>2,'fid'=>1,'name'=>'河南'],
    ['id'=>3,'fid'=>2,'name'=>'鄭州'],
    ['id'=>4,'fid'=>1,'name'=>'廣東'],
    ['id'=>5,'fid'=>4,'name'=>'深圳'],
];

function getList($data, $fid = 0, &$result = array(), $deep = 0)
{
    $deep += 1;
    foreach($data as $key => $val)   //遍歷之后蔬将,此處的$key是鍵(其實(shí)是數(shù)組的序列號(hào))爷速,$val是一條數(shù)據(jù)結(jié)果
    {
        if($fid == $val['fid'])
        {
            $result[$key]['id'] = $val['id'];
            $result[$key]['name'] = "|".str_repeat("--", $deep).$val['name'];
            $result[$key]['fid'] = $val['fid'];
            getList($data, $val['id'], $result, $deep);
        }
    }
    return $result;
}
$res = getList($contry);
echo json_encode($res);

根據(jù)層級(jí)添加空格

  /**
     * 格式化用戶(hù)的菜單輸出數(shù)據(jù)
     * @param $menus \App\Models\System\Menu[]
     * @param int $parentId 父節(jié)點(diǎn)ID
     * @return array
     */
    protected function formatRoleMenu($menus, $parentId = 0, $adds='')
    {
        static $arr = [];
        $j = $k = '';
        $j .= ' ├─ ';
        $k = $adds ? ' ├─ ' : '';
        $depth = $adds ? $adds . $j : '';
        $space = ' ';
        foreach ($menus as $key => $menu) {
            if ($menu->getParentId() == $parentId) {
                $arr[$key] = [
                    'menu_id' => $menu->getId(),
                    'parent_id' => $menu->getParentId(),
                    'title' => $depth . $menu->getTitle(),
                    'icon' => $menu->getIcon(),
                    'route' => $menu->getRoute(),
                    'is_hide' => $menu->getIsHide(),
                ];
                unset($menus[$key]);
                $this->formatRoleMenu($menus, $menu->getId(), $adds . $k . $space);
            }
        }
        return $arr;
    }

優(yōu)化之后

    protected function formatRoleMenu($menus, $parentId = 0, $deep = -1)
    {
        static $arr = [];
       $deep += 1;

        foreach ($menus as $key => $menu) {
            if ($menu->getParentId() == $parentId) {
                $arr[$key] = [
                    'menu_id' => $menu->getId(),
                    'parent_id' => $menu->getParentId(),
                    'title' => $depth . $menu->getTitle(),
                    'icon' => $menu->getIcon(),
                    'route' => $menu->getRoute(),
                    'is_hide' => $menu->getIsHide(),
     // 可能str_repeat()函數(shù)不能直接識(shí)別空格,所以此處使用 &nbsp; 代替一個(gè)空格
                    'depth' => str_repeat('&nbsp;&nbsp;&nbsp;&nbsp;' . '├─ ', $deep),
                ];
                unset($menus[$key]);
=
                $this->formatRoleMenu($menus, $menu->getId(), $adds . $k . $space);
            }
        }
        return $arr;
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末霞怀,一起剝皮案震驚了整個(gè)濱河市惫东,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌毙石,老刑警劉巖廉沮,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異徐矩,居然都是意外死亡滞时,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)滤灯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)坪稽,“玉大人,你說(shuō)我怎么就攤上這事鳞骤≈习伲” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵豫尽,是天一觀的道長(zhǎng)贝咙。 經(jīng)常有香客問(wèn)我,道長(zhǎng)拂募,這世上最難降的妖魔是什么庭猩? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮陈症,結(jié)果婚禮上蔼水,老公的妹妹穿的比我還像新娘。我一直安慰自己录肯,他們只是感情好趴腋,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著论咏,像睡著了一般优炬。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上厅贪,一...
    開(kāi)封第一講書(shū)人閱讀 51,688評(píng)論 1 305
  • 那天蠢护,我揣著相機(jī)與錄音,去河邊找鬼养涮。 笑死葵硕,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的贯吓。 我是一名探鬼主播懈凹,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼悄谐!你這毒婦竟也來(lái)了介评?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤爬舰,失蹤者是張志新(化名)和其女友劉穎们陆,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體洼专,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡棒掠,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了屁商。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片烟很。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖蜡镶,靈堂內(nèi)的尸體忽然破棺而出雾袱,到底是詐尸還是另有隱情,我是刑警寧澤官还,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布芹橡,位于F島的核電站,受9級(jí)特大地震影響望伦,放射性物質(zhì)發(fā)生泄漏林说。R本人自食惡果不足惜煎殷,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望腿箩。 院中可真熱鬧豪直,春花似錦、人聲如沸珠移。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)钧惧。三九已至暇韧,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間浓瞪,已是汗流浹背懈玻。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留追逮,地道東北人酪刀。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像钮孵,于是被迫代替她去往敵國(guó)和親骂倘。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 背景 一年多以前我在知乎上答了有關(guān)LeetCode的問(wèn)題, 分享了一些自己做題目的經(jīng)驗(yàn)巴席。 張土汪:刷leetcod...
    土汪閱讀 12,747評(píng)論 0 33
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理历涝,服務(wù)發(fā)現(xiàn),斷路器漾唉,智...
    卡卡羅2017閱讀 134,659評(píng)論 18 139
  • clojure 新手指南-目錄 - climbdream的個(gè)人空間 - 開(kāi)源中國(guó)社區(qū)https://my.osch...
    葡萄喃喃囈語(yǔ)閱讀 2,262評(píng)論 0 3
  • 引言 秋高氣爽赵刑,天氣轉(zhuǎn)涼分衫,正是學(xué)習(xí)工作做的好時(shí)候。(~ ̄▽?zhuān)?~~(~ ̄▽?zhuān)?~ 我是個(gè)phper最近在寫(xiě)微信支付...
    恩就是這個(gè)名閱讀 7,969評(píng)論 2 15
  • 通過(guò)CAKeyframeAnimation類(lèi)實(shí)現(xiàn)視圖的伸縮變換般此。 沒(méi)有封裝蚪战,直接實(shí)現(xiàn)效果,有需要用的自己加工一下铐懊。
    追沐閱讀 568評(píng)論 0 0