php 无限分类的实现
PHP 无限分类的实现方法
无限分类是一种常见的树形结构数据存储方式,适用于多级分类、菜单管理等场景。以下是几种实现方法:
邻接列表模型(Adjacency List)
邻接列表是最简单的实现方式,通过parent_id字段记录父级分类ID。
CREATE TABLE categories (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
parent_id INT DEFAULT NULL,
FOREIGN KEY (parent_id) REFERENCES categories(id)
);
查询子分类的PHP代码示例:
function getChildren($parentId = 0) {
$db = new PDO('mysql:host=localhost;dbname=test', 'username', 'password');
$stmt = $db->prepare("SELECT * FROM categories WHERE parent_id = ?");
$stmt->execute([$parentId]);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
路径枚举(Path Enumeration)
通过在分类中存储完整路径来优化查询效率。
CREATE TABLE categories (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
path VARCHAR(255) NOT NULL COMMENT '如1/2/3'
);
查询示例:
function getByPath($path) {
$db = new PDO('mysql:host=localhost;dbname=test', 'username', 'password');
$stmt = $db->prepare("SELECT * FROM categories WHERE path LIKE ? ORDER BY path");
$stmt->execute(["$path%"]);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
嵌套集模型(Nested Set)
使用left和right值来表示节点在树中的位置。
CREATE TABLE categories (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
lft INT NOT NULL,
rgt INT NOT NULL
);
查询子树示例:
function getSubTree($nodeId) {
$db = new PDO('mysql:host=localhost;dbname=test', 'username', 'password');
$stmt = $db->prepare("
SELECT node.*
FROM categories AS node, categories AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
AND parent.id = ?
ORDER BY node.lft
");
$stmt->execute([$nodeId]);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
闭包表(Closure Table)
使用单独的表存储节点关系,适合频繁查询的场景。
CREATE TABLE categories (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL
);
CREATE TABLE category_relations (
ancestor INT NOT NULL,
descendant INT NOT NULL,
depth INT NOT NULL,
PRIMARY KEY (ancestor, descendant),
FOREIGN KEY (ancestor) REFERENCES categories(id),
FOREIGN KEY (descendant) REFERENCES categories(id)
);
查询后代节点:
function getDescendants($categoryId) {
$db = new PDO('mysql:host=localhost;dbname=test', 'username', 'password');
$stmt = $db->prepare("
SELECT c.*
FROM categories c
JOIN category_relations cr ON c.id = cr.descendant
WHERE cr.ancestor = ? AND cr.descendant != ?
");
$stmt->execute([$categoryId, $categoryId]);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
递归处理分类数据
对于邻接列表模型,可以使用递归方式获取完整树结构:
function buildTree(array $elements, $parentId = 0) {
$branch = [];
foreach ($elements as $element) {
if ($element['parent_id'] == $parentId) {
$children = buildTree($elements, $element['id']);
if ($children) {
$element['children'] = $children;
}
$branch[] = $element;
}
}
return $branch;
}
// 使用方式
$categories = $db->query("SELECT * FROM categories")->fetchAll(PDO::FETCH_ASSOC);
$tree = buildTree($categories);
每种方法各有优缺点:邻接列表简单但查询效率低,嵌套集查询高效但更新复杂,闭包表最灵活但占用空间大。应根据实际业务需求选择合适方案。







