介绍
laravel-nestedset(Nested Set Model)
安装
composer require kalnoy/nestedset
用法
// database/migrations/2020_08_10_155445_create_category_table.php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; class CreateCategoryTable extends Migration { public function up() { Schema::create('category', function (Blueprint $table) { $table->id(); $table->integer('parent_id')->nullable()->comment('所属分类'); $table->string('name')->comment('级联名称'); $table->string('sign')->comment('级联标识'); $table->string('code')->nullable()->comment('枚举值'); $table->integer('order')->comment('排序位置')->default(50); $table->boolean('enabled')->default(true)->comment('是否启用'); $table->boolean('locked')->default(false)->comment('系统内置'); $table->softDeletes(); $table->timestamps(); // $table->nestedSet(); // 默认的字段名为:_lft、_rgt、parent_id $table->unsignedInteger('_lft'); $table->unsignedInteger('_rgt'); $table->index('parent_id'); $table->index(['sign', 'code']); }); } public function down() { Schema::dropIfExists('category'); } }
// app/Cascade.php namespace App; use DateTimeInterface; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; use Kalnoy\Nestedset\NodeTrait; class Cascade extends Model { use NodeTrait, SoftDeletes; public function getLftName() { return 'left'; } public function getRgtName() { return 'right'; } public function getParentIdName() { return 'parent'; } }
创建节点
Category::create($attributes); $node = new Category($attributes); $node->save(); // save为一个根节点(root)
编辑节点
// 设为根节点 $node->saveAsRoot(); // or: $node->makeRoot()->save(); // 添加子节点 $node->appendToNode($parent)->save(); $parent->appendNode($node); $parent->children()->create($attributes); $node->parent()->associate($parent)->save(); $node->parent_id = $parent->id; $node->save(); // 添加父节点 $node->prependToNode($parent)->save(); $parent->prependNode($node); // 插入节点(将$node添加为指定节点$neighbor的相邻节点,$neighbor必须存在,$node可以为新创建的节点,也可以为已存在的,如果$node为已存在的节点,它将移动到新的位置与$neighbor相邻,必要时它的父级将改变。) # 显性save $node->afterNode($neighbor)->save(); $node->beforeNode($neighbor)->save(); # 隐性 save $node->insertAfterNode($neighbor); $node->insertBeforeNode($neighbor); //删除节点 $node->delete(); // 节点的所有后代将一并删除 Category::where('id', '=', $id)->delete(); // 禁止这样删除!!这将破坏树结构
祖先和后代
$node->ancestors; // 父节点 $node->descendants; // 子节点 $result = Category::ancestorsOf($id); $result = Category::defaultOrder()->ancestorsOf($id); // 按层级排序 $result = Category::reversed()->ancestorsOf($id); // 倒序 $result = Category::ancestorsAndSelf($id); $result = Category::descendantsOf($id); $result = Category::descendantsAndSelf($id); $bool = $node->isRoot(); // 是否为根节点 $bool = $node->isDescendantOf($parent); // 是否为其他节点的子节点 $node->isChildOf($other); $node->isAncestorOf($other); $node->isSiblingOf($other); $node->isLeaf() $bool = Category::isBroken(); // 检查树是否被破环 $data = Category::countErrors(); // 获取错误统计 Node::fixTree();
兄弟节点
有相同父节点的节点
$result = $node->getSiblings(); $result = $node->siblings()->get(); $result = $node->getNextSibling(); // 获取相邻的下一个兄弟节点 $result = $node->getNextSiblings(); // 获取后面的所有兄弟节点 $result = $node->nextSiblings()->get(); // 使用查询获得所有兄弟节点 $result = $node->getPrevSibling(); // 获取相邻的前一个兄弟节点 $result = $node->getPrevSiblings(); // 获取前面的所有兄弟节点 $result = $node->prevSiblings()->get(); // 使用查询获得所有兄弟节点
构建树
$nodes = Category::get()->toFlatTree(); // 一个扁平树 $root = Category::descendantsAndSelf($rootId)->toTree()->first(); $tree = Category::descendantsOf($rootId)->toTree($rootId); // 将数组构建为树 $node = Category::create([ 'name' => 'Foo', 'children' => [ [ 'name' => 'Bar', 'children' => [ [ 'name' => 'Baz' ], ], ], ], ]);