Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PHP+Mysql无限分类 #25

Open
lovecn opened this issue May 26, 2015 · 1 comment
Open

PHP+Mysql无限分类 #25

lovecn opened this issue May 26, 2015 · 1 comment

Comments

@lovecn
Copy link
Owner

lovecn commented May 26, 2015

第一种方法

这种方法是很常见、很传统的一种,先看表结构

表:category
id int 主键,自增
name varchar 分类名称
pid int 父类id,默认0
顶级分类的 pid 默认就是0了。当我们想取出某个分类的子分类树的时候,基本思路就是递归,当然,出于效率问题不建议每次递归都查询数据库,通常的做法是先讲所有分类取出来,保存到PHP数组里,再进行处理,最后还可以将结果缓存起来以提高下次请求的效率。

先来构建一个原始数组,这个直接从数据库中拉出来就行:

$categories = array(
    array('id'=>1,'name'=>'电脑','pid'=>0),
    array('id'=>2,'name'=>'手机','pid'=>0),
    array('id'=>3,'name'=>'笔记本','pid'=>1),
    array('id'=>4,'name'=>'台式机','pid'=>1),
    array('id'=>5,'name'=>'智能机','pid'=>2),
    array('id'=>6,'name'=>'功能机','pid'=>2),
    array('id'=>7,'name'=>'超级本','pid'=>3),
    array('id'=>8,'name'=>'游戏本','pid'=>3),
);
目标是将它转化为下面这种结构

电脑
    笔记本
        超级本
        游戏本
    台式机
手机
    智能机
    功能机
用数组来表示的话,可以增加一个 children 键来存储它的子分类:

array(
    //1对应id,方便直接读取
    1 => array(
        'id'=>1,
        'name'=>'电脑',
        'pid'=>0,
        children=>array(
            &array(
                'id'=>3,
                'name'=>'笔记本',
                'pid'=>1,
                'children'=>array(
                    //此处省略
                )
            ),
            &array(
                'id'=>4,
                'name'=>'台式机',
                'pid'=>1,
                'children'=>array(
                    //此处省略
                )
            ),
        )
    ),
    //其他分类省略
)
处理过程:

$tree = array();
//第一步,将分类id作为数组key,并创建children单元
foreach($categories as $category){
    $tree[$category['id']] = $category;
    $tree[$category['id']]['children'] = array();
}

//第二部,利用引用,将每个分类添加到父类children数组中,这样一次遍历即可形成树形结构。
foreach ($tree as $k=>$item) {
    if ($item['pid'] != 0) {
        $tree[$item['pid']]['children'][] = &$tree[$k];
    }
}

print_r($tree);
打印结果如下:

Array
(
    [1] => Array
        (
            [id] => 1
            [name] => 电脑
            [pid] => 0
            [children] => Array
                (
                    [0] => Array
                        (
                            [id] => 3
                            [name] => 笔记本
                            [pid] => 1
                            [children] => Array
                                (
                                    [0] => Array
                                        (
                                            [id] => 7
                                            [name] => 超级本
                                            [pid] => 3
                                            [children] => Array
                                                (
                                                )

                                        )

                                    [1] => Array
                                        (
                                            [id] => 8
                                            [name] => 游戏本
                                            [pid] => 3
                                            [children] => Array
                                                (
                                                )

                                        )

                                )

                        )

                    [1] => Array
                        (
                            [id] => 4
                            [name] => 台式机
                            [pid] => 1
                            [children] => Array
                                (
                                )

                        )

                )

        )

    [2] => Array
        (
            [id] => 2
            [name] => 手机
            [pid] => 0
            [children] => Array
                (
                    [0] => Array
                        (
                            [id] => 5
                            [name] => 智能机
                            [pid] => 2
                            [children] => Array
                                (
                                )

                        )

                    [1] => Array
                        (
                            [id] => 6
                            [name] => 功能机
                            [pid] => 2
                            [children] => Array
                                (
                                )

                        )

                )

        )

    [3] => Array
        (
            [id] => 3
            [name] => 笔记本
            [pid] => 1
            [children] => Array
                (
                    [0] => Array
                        (
                            [id] => 7
                            [name] => 超级本
                            [pid] => 3
                            [children] => Array
                                (
                                )

                        )

                    [1] => Array
                        (
                            [id] => 8
                            [name] => 游戏本
                            [pid] => 3
                            [children] => Array
                                (
                                )

                        )

                )

        )

    [4] => Array
        (
            [id] => 4
            [name] => 台式机
            [pid] => 1
            [children] => Array
                (
                )

        )

    [5] => Array
        (
            [id] => 5
            [name] => 智能机
            [pid] => 2
            [children] => Array
                (
                )

        )

    [6] => Array
        (
            [id] => 6
            [name] => 功能机
            [pid] => 2
            [children] => Array
                (
                )

        )

    [7] => Array
        (
            [id] => 7
            [name] => 超级本
            [pid] => 3
            [children] => Array
                (
                )

        )

    [8] => Array
        (
            [id] => 8
            [name] => 游戏本
            [pid] => 3
            [children] => Array
                (
                )

        )

)
优点:关系清楚,修改上下级关系简单。

缺点:使用PHP处理,如果分类数量庞大,效率也会降低。

第二种方法

这种方法是在表字段中增加一个path字段:

表:category
id int 主键,自增
name varchar 分类名称
pid int 父类id,默认0
path varchar 路径
示例数据:

id        name        pid        path
1         电脑        0          0
2         手机        0          0
3         笔记本      1          0-1
4         超级本      3          0-1-3
5         游戏本      3          0-1-3
path字段记录了从根分类到上一级父类的路径,用id+'-'表示。

这种方式,假设我们要查询电脑下的所有后代分类,只需要一条sql语句:

select id,name,path from category where path like (select concat(path,'-',id,'%') as path from category where id=1);
结果:

+----+-----------+-------+
| id | name      | path  |
+----+-----------+-------+
| 3  | 笔记本 | 0-1   |
| 4  | 超级本 | 0-1-3 |
| 5  | 游戏本 | 0-1-3 |
+----+-----------+-------+
这种方式也被很多人所采纳,我总结了下:

优点:查询容易,效率高,path字段可以加索引。

缺点:更新节点关系麻烦,需要更新所有后辈的path字段。
//http://ekan001.com/article/25


class tree { 
    //访问index查看树形结构  http://www.manks.top/php-tree-deep.html
    //获取树 https://segmentfault.com/q/1010000007205669
    public static function getTree () { 
        //这里我们直接获取所有的数据,然后通过程序进行处理 
        //在无限极分类中最忌讳的是对数据库进行层层操作,也就很容易造成内存溢出 
        //最后电脑死机的结果 
        $data = $categories = array(
          array('id'=>1,'name'=>'电脑','pid'=>0),
          array('id'=>2,'name'=>'手机','pid'=>0),
          array('id'=>3,'name'=>'笔记本','pid'=>1),
          array('id'=>4,'name'=>'台式机','pid'=>1),
          array('id'=>5,'name'=>'智能机','pid'=>2),
          array('id'=>6,'name'=>'功能机','pid'=>2),
          array('id'=>7,'name'=>'超级本','pid'=>3),
          array('id'=>8,'name'=>'游戏本','pid'=>3),
      );
        return self::_generateTree($data); 
    } 
    //生成树 
    private static function _generateTree ($data, $pid = 0) { 
        $tree = []; 
        if ($data && is_array($data)) { 
            foreach($data as $v) { 
                if($v['pid'] == $pid) { 
                    $tree[] = [ 
                        'id' => $v['id'], 
                        'name' => $v['name'], 
                        'pid' => $v['pid'], 
                        'children' => self::_generateTree($data, $v['id']), 
                    ]; 
                } 
            } 
        } 
        return $tree; 
    } 
    public $cats = array();    
        
    public function category($fid=0, $level=1) {
        $sql = "SELECT * FROM `category` WHERE pid=:pid";
        $pdo = new PDO("mysql:host=localhost;dbname=test", "root", null);
        try {
            $stmt = $pdo->prepare($sql);
            $stmt -> bindParam(":pid", $fid, PDO::PARAM_INT);
            $stmt -> execute();
            $results = $stmt->fetchAll(PDO::FETCH_ASSOC);
            foreach ($results as $v) {
                array_push($this->cats, array($v['name'], $level));
                $this-> category($v['id'], $level+1, $this->cats);
                
            }

            return $this->cats;
            
        } catch(Exception $e) {
            die($e->getMessage());
        }
    }

    public function category2($fid=0, $level=1, $cats = array()) {
        $sql = "SELECT * FROM `category` WHERE pid=:pid";
        $pdo = new PDO("mysql:host=localhost;dbname=test", "root", null);
        try {
            $stmt = $pdo->prepare($sql);
            $stmt -> bindParam(":pid", $fid, PDO::PARAM_INT);
            $stmt -> execute();
            $results = $stmt->fetchAll(PDO::FETCH_ASSOC);
            foreach ($results as $v) {
                array_push($cats, array($v['name'], $level));
                $this-> category($v['id'], $level+1, $cats);
                
            }

            return $cats;
            
        } catch(Exception $e) {
            die($e->getMessage());
        }
    }
}
print_r(tree::getTree());
print_r((new tree)->category());
print_r((new tree)->category2());


/*
CREATE TABLE `category` (
 `id` int(11) NOT NULL,
 `name` varchar(50) CHARACTER SET utf8 NOT NULL,
 `pid` int(11) NOT NULL DEFAULT '0',
 PRIMARY KEY (`id`)
 )ENGINE=InnoDB DEFAULT CHARSET=utf8;
 +----+--------+-----
| id | name   | pid
+----+--------+-----
|  1 | 电脑   |   0
|  2 | 手机   |   0
|  3 | 笔记本 |   1
|  4 | 台式机 |   1
|  5 | 智能机 |   2
|  6 | 功能机 |   2
|  7 | 超级本 |   3
|  8 | 游戏本 |   3
+----+--------+-----
 */
$pdo = new PDO("mysql:host=localhost;dbname=test", "root", null);
$sql = 'SELECT * FROM `category`';
    $stmt = $pdo->prepare($sql);
    $stmt->execute();
    $data = $stmt->fetchAll(PDO::FETCH_ASSOC);
    print_r($data);
function getCategories(PDO $pdo, $pid = 0)
{
    $sql = 'SELECT * FROM `category` WHERE pid=:pid';
    $stmt = $pdo->prepare($sql);
    $stmt->bindParam(':pid', $pid, PDO::PARAM_INT);
    $stmt->execute();
    $data = $stmt->fetchAll(PDO::FETCH_ASSOC);
    foreach ($data as &$row) {
        $row['children'] = getCategories($pdo, $row['id']);
    }
    return $data;
}

$a = getCategories($pdo);
@lovecn
Copy link
Owner Author

lovecn commented Nov 19, 2016

https://segmentfault.com/q/1010000007523914 使用MSQL循环语句查找所有下级
image
image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant