首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP + MySQL 實現無限級分類:完整類庫與資料庫設計

     編輯:PHP 相關知識

PHP + MySQL 實現無限級分類:完整類庫與資料庫設計

無限級分類是 CMS、電商、論壇等系統的核心功能。本文提供一個功能完整的 PHP 類庫,支援新增、刪除、編輯、移動節點,並包含資料表結構與前台展示範例,開箱即用。

資料庫設計

採用鄰接表模式,結構簡單高效:

CREATE TABLE `p_newsclass` (
  `id` int(7) NOT NULL AUTO_INCREMENT,
  `f_id` int(7) NOT NULL DEFAULT 0 COMMENT '父級 ID,0 為頂層',
  `name` varchar(255) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- 示例資料
INSERT INTO `p_newsclass` (`id`, `f_id`, `name`) VALUES
(3, 0, '中國'),
(4, 3, '福建'),
(5, 4, '龍巖市'),
(7, 4, '廈門市'),
(9, 5, '漳平市');

核心類別 SortClass

<?php
class SortClass {
    var $data = [];      // id => name
    var $child = [-1 => []]; // parent_id => [child_ids]
    var $layer = [-1 => -1]; // id => depth
    var $parent = [];    // id => parent_id
    var $link;           // DB 連線資源
    var $table;          // 資料表名

    function __construct($link, $table) {
        $this->setNode(0, -1, '頂極節點'); // 虛擬根節點
        $this->link = $link;
        $this->table = $table;
        
        $results = mysql_query("SELECT * FROM `$this->table`", $this->link);
        while ($node = mysql_fetch_assoc($results)) {
            $this->setNode($node['id'], $node['f_id'], $node['name']);
        }
    }

    // 註冊節點到記憶體結構
    function setNode($id, $parent, $value) {
        $parent = $parent ? $parent : 0;
        $this->data[$id] = $value;
        $this->child[$id] = [];
        $this->child[$parent][] = $id;
        $this->parent[$id] = $parent;
        $this->layer[$id] = isset($this->layer[$parent]) ? $this->layer[$parent] + 1 : 0;
    }

    // 遞迴取得子樹 ID 列表 (前序遍歷)
    function getList(&$tree, $root = 0) {
        foreach ($this->child[$root] as $id) {
            $tree[] = $id;
            if ($this->child[$id]) $this->getList($tree, $id);
        }
    }

    // 公開 API
    function getValue($id) { return $this->data[$id]; }
    function getLayer($id, $space = false) { 
        return $space ? str_repeat($space, $this->layer[$id]) : $this->layer[$id]; 
    }
    function getParent($id) { return $this->parent[$id]; }
    function getParents($id) { /* 取得所有祖先鏈 */ }
    function getChild($id) { return $this->child[$id]; }
    function getChilds($id = 0) { $arr = [$id]; $this->getList($arr, $id); return $arr; }

    // CRUD 操作
    function addNode($name, $pid) {
        mysql_query("INSERT INTO `$this->table` (`f_id`,`name`) VALUES ('$pid','$name')", $this->link);
    }
    function modNode($cid, $newname) {
        mysql_query("UPDATE `$this->table` SET `name`='$newname' WHERE `id`=$cid", $this->link);
    }
    function delNode($cid) {
        $all = $this->getChilds($cid);
        $sql = empty($all) 
            ? "DELETE FROM `$this->table` WHERE `id`=$cid"
            : "DELETE FROM `$this->table` WHERE `id` IN (" . implode(',', $all) . ",$cid)";
        mysql_query($sql, $this->link);
    }
    function moveNode($cid, $topid) {
        mysql_query("UPDATE `$this->table` SET `f_id`=$topid WHERE `id`=$cid", $this->link);
    }
}
?>

前台操作介面範例

<?php
// 連線資料庫 (建議改用 PDO/MySQLi)
$link = mysql_connect('localhost','root','') or die(mysql_error());
mysql_select_db('class', $link) or die(mysql_error());
mysql_query("SET NAMES utf8");

$tree = new SortClass($link, '`p_newsclass`');

// 處理表單提交
$op = $_POST['op'] ?? $_GET['op'] ?? '';
if ($op) {
    switch ($op) {
        case 'add': $tree->addNode($_POST['cname'], $_POST['pid']); break;
        case 'mod': $tree->modNode($_POST['cid'], $_POST['cname']); break;
        case 'del': $tree->delNode($_GET['cid']); break;
        case 'move': $tree->moveNode($_POST['who'], $_POST['to']); break;
    }
    // 重導向防止重複提交
    header("Location: " . $_SERVER['PHP_SELF'] . "?" . time());
    exit;
}

$category = $tree->getChilds(); // 取得完整樹結構 ID 列表
?>
<!DOCTYPE html>
<html>
<head>
<style> body{font-size:12px;} ul{list-style:none;} a{cursor:pointer;} </style>
<script> function $(e){return document.getElementById(e);} function mod(cid){ $('cid').value=cid; $('op').value='mod'; $('name').style.border='1px solid red'; } </script>
</head>
<body>
<h3>新增/編輯分類</h3>
<form method="post">
    名稱: <input type="text" id="name" name="cname" /> 
    添加到: <?= makeSelect($category, 'pid') ?><br />
    <input type="hidden" id="op" name="op" value="add" />
    <input type="hidden" id="cid" name="cid" />
    <input type="submit" value="提交" />
</form>

<h3>移動分類</h3>
<form method="post">
    <?= makeSelect($category, 'who') ?> 移動到: <?= makeSelect($category, 'to') ?>
    <input type="hidden" name="op" value="move" />
    <input type="submit" value="提交" />
</form>

<h3>分類列表</h3>
<ul>
<?php foreach ($category as $id): ?>
    <li>
        <?= $tree->getLayer($id, '|- ') ?> <?= $tree->getValue($id) ?>
        <a href="?op=del&cid=<?=$id?>" onclick="return confirm('確定刪除?')">刪除</a>
        <a onclick="mod(<?=$id?>)">編輯</a>
    </li>
<?php endforeach; ?>
</ul>

<?php
// 輔助函數:生成下拉選單
function makeSelect($array, $formname) {
    global $tree;
    $html = '<select name="' . $formname . '">';
    foreach ($array as $id) {
        $html .= '<option value="' . $id . '">' . $tree->getLayer($id, '|-') . $tree->getValue($id) . '</option>';
    }
    return $html . '</select>';
}
?>
</body>
</html>

優化建議與現代化重構方向

  • 資料庫驅動: 將 mysql_* 替換為 PDO 或 MySQLi,使用預處理語句防止 SQL 注入。
  • 樹狀結構優化: 資料量大時,考慮改用 Closure Table (關係閉包表) 或 Nested Set (嵌套集合) 模型,查詢子樹/祖先路徑更高效。
  • 快取機制: 將構建好的樹結構快取至 Redis/Memcached,減少資料庫查詢與 PHP 遞迴開銷。
  • 前端交互: 採用 AJAX + JSON API,配合 zTree、jstree 等 JS 庫實現拖拽排序、懶加載等豐富體驗。

這個類庫展示了無限分類的核心邏輯,理解其遞迴構建與陣列映射原理,您即可根據專案需求進行擴展與優化。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved