php 遞歸無限分類

參考
因為遞歸的效率相對來說比較低,所以很少使用,尤其是在查詢數據庫的時候對數據庫的長時間占用很不合理。

但是遞歸遍歷無限分類列表還是相當重要的,而且還可以對數據進行格式化處理,如果嫌棄效率問題,本人給你一個好建議就是緩存。比如當用戶登錄的時候判斷完用戶的權限后獲取用戶可以操作的菜單,然后遞歸格式化菜單分類列表,然后緩存起來,這樣用戶之后的操作都不會涉及到遞歸獲取菜單,效率可以大大提升。

個人理解 。其實遞歸分類的本質是
1.把一個表里面的所有數據都取出來
2-1.如果fid ==val['fid']
2-2.把數據遍歷后,重新組合。把名字根據分類級別,添加n倍“---”
2-3.添加完之后,還有遞歸調用本函數

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

  • 1.在項目的Common目錄中的function.php中添加以下函數:
/**
 *
 * @param array $data 結果集 (整個表的結果)
 * @param int $fid 父類ID
 * @param array $result 結果數據
 * @param int $deep 分類級數
 * @return array
 */
function getList($data, $fid = 0, &$result = array(), $deep = 0)
{
    $deep += 1;
    foreach($data as $key => $val)   //遍歷之后,此處的$key是鍵(其實是數組的序列號),$val是一條數據結果
    {
    $val = $this->objectToArray($val);   //注意,此處$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;
}
  • 2.在需要的地方直接getList($data)就可以,比如在Controller中:
public function test()
    {
        $data = \DB::table('test')->get();

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

    }
數據表
dd($data)結果
dd($newClass,查看getList()函數的執行順序)
echo $key."\n"結果
print_r($val)."\n";結果
$newClass = $this->getList($data); dd($newClass); 報錯
dd($newClass);結果
getList()函數中,如果$result不加傳指符$,dd($newClass)結果)
  • 3.前臺直接遍歷輸出就可以了。
<select name="class">
   @foreach($newClass as  $v)
        <option value="{{ $v['id'] }}">{{$v['name']}}</option>
   @endforeach
</select>
效果圖
  • 數據結構
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;

補充

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

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

}
這個執行會報錯,原因是遍歷$data后的$val值是對象。所以我們需要一個對象轉數組的執行

寫的有點亂,主要是為了展示效果圖。下面把源碼重新整理下

// 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 結果集 (整個表的結果)
     * @param int $fid 父類ID
     * @param array $result 結果數據
     * @param int $deep 分類級數
     * @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;
    }
    /*
     * 數組轉對象
     */
    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數組定義成靜態方法。靜態方法能夠保存原來的數據,但是如果不使用靜態方法,result=array();相當于每次都把$result定義成數組,并賦值為空



   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=[]呢,這是因為我們的函數里面,不僅有if,還有else。
也就是說當if不存在時,我們執行的代碼就是
foreach($data as $key => $val)
        {
            $val = $this->objectToArray($val);
           
        }
        return $result;

這個時候,我們根本沒有給$result定義,也沒有給它賦值,當然就會報錯
如果想要解決這個問題,我們只能做個測試,來一個else,然后給$result賦值

但是這樣還是不能拼接我們獲取的值,我只是說我們解決這種情況的處理。
但是放倒全局來說,所以我們還是用static或者傳指符吧

$contry = [
    ['id'=>1,'fid'=>0,'name'=>'國內'],
    ['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是鍵(其實是數組的序列號),$val是一條數據結果
    {
        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);

根據層級添加空格

  /**
     * 格式化用戶的菜單輸出數據
     * @param $menus \App\Models\System\Menu[]
     * @param int $parentId 父節點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;
    }

優化之后

    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()函數不能直接識別空格,所以此處使用 &nbsp; 代替一個空格
                    'depth' => str_repeat('&nbsp;&nbsp;&nbsp;&nbsp;' . '├─ ', $deep),
                ];
                unset($menus[$key]);
=
                $this->formatRoleMenu($menus, $menu->getId(), $adds . $k . $space);
            }
        }
        return $arr;
    }
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容