介绍
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' ],
],
],
],
]);

