參考
因?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.如果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=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í)別空格,所以此處使用 代替一個(gè)空格
'depth' => str_repeat(' ' . '├─ ', $deep),
];
unset($menus[$key]);
=
$this->formatRoleMenu($menus, $menu->getId(), $adds . $k . $space);
}
}
return $arr;
}