分层数据Hierarchical Data探索(1.递归)

分层数据Hierarchical Data探索(例如:无限级分类、多级菜单、省份城市)

引言

什么是分层数据?
类似于树形结构,除了根节点和叶子节点外,所有节点都有一个父节点和一个或多个子节点。
大多数同学都曾在数据库中处理过分层数据(hierarchical data),分层数据存在于许多基于数据库的应用程序中,包括论坛和邮件列表中的分类、商业组织图表、内容管理系统的分类、产品分类、无限级分类、多级菜单、省份城市等等。但是因为关系数据库中的表没有层次关系,只是简单的平面化的列表;而分层数据具有父-子关系,显然关系数据库中的表不能自然地表现出其分层的特性。

接下来我会先通过一般方法和递归( recursion
)方法来实现无限极分类,然后再通过 两种数据模型
来谈一谈分层数据的处理。

三种方式的变量传递

  • & 引用赋值
function doloop1(&$i = 1)
{
    print_r($i);
    $i++;
    if ($i <= 10) {
        doloop1($i);
    }
}
doloop1();
  • static 静态变量
function doloop2()
{
    static $i = 1;
    print_r($i);
    $i++;
    if ($i <= 10) {
        doloop2();
    }
}

doloop2();
  • global 全局变量
$i = 1;
function doloop3()
{
    global $i;
    echo $i;
    $i++;
    if ($i <= 10) {
        doloop3();
    }
}
doloop3();

构建模拟数据

# 模拟数据
       $data = [
            ['id' => 1, 'title' => 'Electronics', 'parent_id' => 0],
            ['id' => 2, 'title' => 'Laptops & PC', 'parent_id' => 1],
            ['id' => 3, 'title' => 'Laptops', 'parent_id' => 2],
            ['id' => 4, 'title' => 'PC', 'parent_id' => 2],
            ['id' => 5, 'title' => 'Cameras & photo', 'parent_id' => 1],
            ['id' => 6, 'title' => 'Camera', 'parent_id' => 5],
            ['id' => 7, 'title' => 'Phones & Accessories', 'parent_id' => 1],
            ['id' => 8, 'title' => 'Smartphones', 'parent_id' => 7],
            ['id' => 9, 'title' => 'Android', 'parent_id' => 8],
            ['id' => 10, 'title' => 'iOS', 'parent_id' => 8],
            ['id' => 11, 'title' => 'Other Smartphones', 'parent_id' => 8],
            ['id' => 12, 'title' => 'Batteries', 'parent_id' => 7],
            ['id' => 13, 'title' => 'Headsets', 'parent_id' => 7],
            ['id' => 14, 'title' => 'Screen Protectors', 'parent_id' => 7],
        ];

获取无限极分类

/**
 * 值引用获取无限极分类树
 * 
 * @param array $data
 * @return array
 */
function make_tree($data)
{  
    $refer = array();  
    $tree = array();  
    foreach($data as $k => $v){  
        $refer[$v['id']] = & $data[$k];  //创建主键的数组引用  
    }  

    foreach($data as $k => $v){  
        $parent_id = $v['parent_id'];   //获取当前分类的父级id  
        if($parent_id == 0){  
            $tree[] = & $data[$k];   //顶级栏目
        }else{  
            if(isset($refer[$parent_id])){  
                $refer[$parent_id]['children'][] = & $data[$k];  //如果存在父级栏目,则添加进父级栏目的子栏目数组中  
            }  
        }  
    }  

    return $tree;  
}

/**
 * 递归获取无限极分类树
 * 
 * @param array $data
 * @param int $parent_id
 * @param int $level
 * @return array
 */
function make_tree2($data = [], $parent_id = 0, $level = 0)
{
    $tree = [];
    if ($data && is_array($data)) {
        foreach ($data as $v) {
            if ($v['parent_id'] == $parent_id) {
                $tree[] = [
                    'id' => $v['id'],
                    'level' => $level,
                    'title' => $v['title'],
                    'parent_id' => $v['parent_id'],
                    'children' => make_tree2($data, $v['id'], $level + 1),
                ];
            }
        }
    }
    return $tree;
}

获取子节点以及节点的层级

/**
 * 引用赋值方式
 * @param array $data
 * @param int $id
 * @param int $level
 * @return array
 */
function getSubTree($data = [], $id = 0, $level = 0)
{
    static $tree = [];

    foreach ($data as $key => $value) {
        if ($value['parent_id'] == $id) {
            $value['laravel'] = $level;
            $tree[] = $value;
            getSubTree($data, $value['id'], $level + 1);
        }
    }
    return $tree;
}

/**
 * 静态变量方式
 * @param array $data
 * @param int $id
 * @param int $level
 * @return array
 */
function getSubTree($data = [], $id = 0, $level = 0)
{
    static $tree = [];

    foreach ($data as $key => $value) {
        if ($value['parent_id'] == $id) {
            $value['laravel'] = $level;
            $tree[] = $value;
            getSubTree($data, $value['id'], $level + 1);
        }
    }
    return $tree;
}


/**
 * 全局变量方式
 * @param array $data
 * @param int $id
 * @param int $level
 * @return array
 */

$tree = [];  //先申明变量
function getSubTree($data = [], $id = 0, $level = 0)
{
    global $tree;

    foreach ($data as $key => $value) {
        if ($value['parent_id'] == $id) {
            $value['laravel'] = $level;
            $tree[] = $value;
            getSubTree($data, $value['id'], $level + 1);
        }
    }
    return $tree;
}

通过pid获取所有上级分类 常用于面包屑导航

/**
 * getParentsByParentId2($categories, 9)
 *
 * @param array $data
 * @param $parent_id
 * @return array
 */
function getParentsByParentId($data = [], $parent_id)
{
    static $categories = [];

    if ($data && is_array($data)) {
        foreach ($data as $item) {
            if ($item['id'] == $parent_id) {
                $categories[] = $item;
                getParentsByParentId($data, $item['parent_id']);
            }
        }
    }
    return $categories;
}

function getParentsByParentId2($data = [], $parent_id)
{
    $categories = [];

    if ($data && is_array($data)) {
        foreach ($data as $item) {
            if ($item['id'] == $parent_id) {
                $categories[] = $item;
                $categories = array_merge($categories, getParentsByParentId2($data, $item['parent_id']));
            }
        }
    }
    return $categories;
}