Kalnoy Nestedset(處理樹狀結構資料)

Kalnoy\Nestedset 套件介紹

Kalnoy\Nestedset 是一個用於處理樹狀結構資料的 Laravel 套件,它實作了「嵌套集合模型(Nested Set Model)」,適合儲存與操作如分類目錄、組織架構等階層資料。

優點

  • 高效能的樹狀結構儲存:
    使用 Nested Set 模型來儲存節點位置,可在 O(1) 時間內查詢整棵樹或子樹,避免多層級 JOIN 操作。

  • 簡單的 API 操作:
    提供如 appendNode()makeChildOf()children() 等常用方法,讓操作如同使用一般 Eloquent 模型一樣方便。

  • 支援樹的遍歷與子樹操作:
    可輕鬆遍歷整棵樹、查詢所有後代或子節點。

  • 支援節點動態調整:
    節點可自由移動至樹的其他位置,套件會自動處理相關索引調整。

  • 資料庫索引效能佳:
    lftrgt 欄位(由 ->nestedSet() 自動建立)支援快速查詢。

  • 支援 Laravel Eloquent ORM:
    與原生 Eloquent 完美整合,易於上手。

  • 自動同步 left/right 欄位:
    插入、移動節點時自動處理索引更新,減少人工操作錯誤。

缺點

  • 寫入操作相對複雜:
    插入、刪除或移動節點時需調整多筆資料的索引,寫入成本高。

  • 需要額外欄位:
    除了基本欄位外,還需 lftrgt(和 parent_id, depth)來表示樹結構。

  • 不適合頻繁變動的樹:
    若節點變動頻繁,每次都需重新計算索引,對效能影響大。

  • 高並發下競爭風險:
    多人同時異動節點位置,可能會產生資料不一致問題,需透過鎖定機制控制。

  • 重建樹結構成本高:
    當樹結構毀損時,可能需要重新建構整棵樹,代價不小。

  • 不適合扁平資料結構:
    若樹只有 1~2 層,Nested Set 的設計反而會造成過度設計。

樹結構範例(商品分類)

電子產品
├── 手機
│   ├── 智能手機
│   │   ├── Apple
│   │   └── Samsung
│   └── 功能手機
├── 電腦
│   ├── 桌面電腦
│   └── 筆記型電腦
家電
├── 冰箱
└── 洗衣機

資料庫結構

Schema::create('categories', function (Blueprint $table) {
    $table->id();
    $table->string('name');
    $table->nestedSet(); // 建立 lft, rgt, parent_id, depth 欄位
    $table->timestamps();
});

模型設定

use Kalnoy\Nestedset\NodeTrait;

class Category extends Model
{
    use NodeTrait;
}

初始化資料

use App\Models\Category;

// 建立根節點
$electronics = Category::create(['name' => '電子產品']);
$homeAppliances = Category::create(['name' => '家電']);

// 建立第二層
$mobile = Category::create(['name' => '手機']);
$computer = Category::create(['name' => '電腦']);
$fridge = Category::create(['name' => '冰箱']);
$washingMachine = Category::create(['name' => '洗衣機']);

$electronics->appendNode($mobile);
$electronics->appendNode($computer);
$homeAppliances->appendNode($fridge);
$homeAppliances->appendNode($washingMachine);

// 建立第三層
$smartphones = Category::create(['name' => '智能手機']);
$featurePhones = Category::create(['name' => '功能手機']);
$desktop = Category::create(['name' => '桌面電腦']);
$laptop = Category::create(['name' => '筆記型電腦']);

$mobile->appendNode($smartphones);
$mobile->appendNode($featurePhones);
$computer->appendNode($desktop);
$computer->appendNode($laptop);

// 第四層品牌
$apple = Category::create(['name' => 'Apple']);
$samsung = Category::create(['name' => 'Samsung']);

$smartphones->appendNode($apple);
$smartphones->appendNode($samsung);

查詢範例

// 查詢電子產品所有後代(含自己)
$electronics = Category::where('name', '電子產品')->first();
$tree = $electronics->descendantsAndSelf;

// 查詢子分類(不含自己)
$mobile = Category::where('name', '手機')->first();
$children = $mobile->children;

// 查詢 Apple 的父節點
$apple = Category::where('name', 'Apple')->first();
$parent = $apple->parent;

// 將 Apple 移動到 功能手機 下
$featurePhones = Category::where('name', '功能手機')->first();
$apple->makeChildOf($featurePhones);

常見應用場景

公司組織架構

總經理
├── 行銷部門
│   ├── 社群行銷
│   └── 搜索行銷
├── 技術部門
│   ├── 前端團隊
│   └── 後端團隊
└── 人事部門

評論系統(階層式)

評論 1
├── 回應 1.1
│   └── 回應 1.1.1
├── 回應 1.2
評論 2

網站選單

首頁
├── 產品
│   ├── A
│   ├── B
├── 服務
│   ├── A
│   └── B
└── 關於我們

權限階層設定

管理員
├── 主管
│   ├── 部門主管
│   └── 團隊主管
└── 員工

官方連結與文件