khazhinov / laravel-trees
Laravel 多树结构
Requires
- php: ^8.0|^8.1
- efureev/support: ^4.13
- illuminate/database: ^8.80|^9.25
- illuminate/events: ^8.80|^9.25
Requires (Dev)
- fakerphp/faker: ^1.20
- orchestra/testbench: ^6.17|^7.6
- phpunit/phpunit: ^9.5
- squizlabs/php_codesniffer: ^3.7
- symfony/var-dumper: ^5.2|^6.1
- dev-master
- v3.7.1
- v3.7.0
- v3.6.2
- v3.6.1
- v3.6.0
- v3.5.3
- v3.5.2
- v3.5.1
- v3.5.0
- v3.4.1
- v3.4.0
- v3.3.3
- v3.3.2
- v3.3.1
- v3.3.0
- v3.2.0
- v3.1.2
- v3.1.1
- v3.1.0
- v3.0.1
- v3.0.0
- v2.5.0
- v2.4.0
- v2.3.2
- v2.3.1
- v2.3.0
- v2.2.0
- v2.1.1
- v2.1.0
- v2.0.2
- v2.0.1
- v2.0.0
- v1.7.5
- v1.7.4
- v1.7.3
- v1.7.2
- v1.7.1
- v1.7.0
- v1.6.4
- v1.6.3
- v1.6.2
- v1.6.1
- 1.6.0
- v1.5.2
- v1.5.1
- v1.5.0
- v1.4.0
- v1.3.6
- v1.3.5
- v1.3.4
- v1.3.3
- v1.3.2
- v1.3.1
- v1.3.0
- v1.2.0
- v1.1.9
- v1.1.8
- v1.1.7
- v1.1.6
- v1.1.5
- v1.1.4
- v1.1.3
- v1.1.2
- v1.1.1
- 1.0.0
- dev-fork-correct-column-names
- dev-next
- dev-php7
This package is not auto-updated.
Last update: 2024-09-25 19:45:40 UTC
README
内容
信息
本包是多树结构(有很多根节点)。
嵌套集是什么?
嵌套集或嵌套集模型是一种在关系表中有效地存储层次数据的方法。来自维基百科
嵌套集模型是按照树遍历的顺序编号节点,遍历每个节点两次,按遍历顺序分配数字,并在两次遍历中。这为每个节点留下两个数字,这两个数字作为两个属性存储。查询变得便宜:可以通过比较这些数字来测试层次结构成员资格。更新需要重新编号,因此很昂贵。
应用
当树很少更新时,NSM 显示出良好的性能。它被调整以快速获取相关节点。它非常适合构建多级菜单或商店分类。
要求
- PHP: ^8.0
- Laravel: ^8.80 | ^9.2
强烈建议使用支持事务的数据库(如MySQL的InnoDB,Postgres)来确保树不会受到可能的损坏。
安装
要安装此包,请在终端中
composer require efureev/laravel-trees
测试
./vendor/bin/phpunit --testdox
或
composer test
文档
此包支持不同的模型主键:int
,uuid
。此包允许创建多根结构:不仅只有一个根!还可以在树之间移动节点。
迁移
单树结构模型
<?php namespace App\Models; use Fureev\Trees\NestedSetTrait; use Illuminate\Database\Eloquent\Model; class Category extends Model { use NestedSetTrait; }
或使用自定义基本配置
<?php namespace App\Models; use Fureev\Trees\{NestedSetTrait,Contracts\TreeConfigurable}; use Fureev\Trees\Config\Base; use Illuminate\Database\Eloquent\Model; class Category extends Model implements TreeConfigurable { use NestedSetTrait; protected static function buildTreeConfig(): Base { return new Base(); } }
或使用自定义配置
protected static function buildTreeConfig(): Base { return Base::make() ->setAttribute('parent', ParentAttribute::make()->setName('papa_id')) ->setAttribute('left', LeftAttribute::make()->setName('left_offset')) ->setAttribute('right', RightAttribute::make()->setName('right_offset')) ->setAttribute('level', LevelAttribute::make()->setName('deeeeep')); }
多树结构模型和主键类型 uuid
<?php namespace App\Models; // use Fureev\Trees\Config\TreeAttribute; use Fureev\Trees\Contracts\TreeConfigurable; use Fureev\Trees\NestedSetTrait; use Fureev\Trees\Config\Base; use Illuminate\Database\Eloquent\Model; class Item extends Model implements TreeConfigurable { use NestedSetTrait; protected $keyType = 'uuid'; protected static function buildTreeConfig(): Base { $config= new Base(true); // $config->parent()->setType('uuid'); <-- `parent type` set up automatically from `$model->keyType` return $config; } /* or: protected static function buildTreeConfig(): Base { return Base(TreeAttribute::make('uuid')->setAutoGenerate(false)); } or: protected static function buildTreeConfig(): Base { return Base::make() ->setAttributeTree(TreeAttribute::make()->setName('big_tree_id')) ->setAttribute('parent', ParentAttribute::make()->setName('pid')) ->setAttribute('left', LeftAttribute::make()->setName('left_offset')) ->setAttribute('right', RightAttribute::make()->setName('right_offset')) ->setAttribute('level', LevelAttribute::make()->setName('deeeeep')); } */ }
在迁移中使用
<?php use Fureev\Trees\Migrate; use Illuminate\Database\Migrations\Migration; class AddTemplates extends Migration { public function up() { Schema::create('trees', function (Blueprint $table) { $table->uuid('id')->primary(); $table->string('title'); Migrate::columns($table, (new Page)->getTreeConfig()); $table->timestamps(); $table->softDeletes(); }); } }
关系
节点具有以下关系,它们是完整功能性的,并且可以预加载
- 节点属于
parent
- 节点有多个
children
- 节点有多个
ancestors
- 节点有多个
descendantsNew
创建节点
创建根节点
当你创建一个根节点时:如果你使用 ...
- 单模式:你可能只能创建一个根节点。
- 多模式:它将作为根节点插入,并具有不同的
tree_id
。默认:递增一次。你可以自定义此功能。
这些操作是相同的
// For single-root tree Category::make($attributes)->makeRoot()->save(); Category::make($attributes)->saveAsRoot(); Category::create(['setRoot'=>true,...]); // For multi-root tree. If parent is absent, node set as root. Page::make($attributes)->save();
创建非根节点
当你创建一个非根节点时,它将被附加到父节点的末尾。
如果你想使节点成为其他节点的子节点,你可以将其作为最后一个或第一个子节点。
在以下示例中,$parent
是某个现有节点。
附加到指定的父节点
将子节点添加到节点中。在父节点的其他子节点之后插入。
$node->appendTo($parent)->save();
预先附加到指定的父节点
将子节点添加到节点中。在父节点的其他子节点之前插入。
$node->prependTo($parent)->save();
在父节点之前插入
将子节点添加到同一父节点。在目标节点之前插入。
$node->insertBefore($parent)->save();
在父节点之后插入
将子节点添加到同一父节点。在目标节点之后插入。
$node->insertAfter($parent)->save();
移动节点
在父节点范围内向上移动节点
$node->up();
在父节点范围内向下移动节点
$node->down();
删除节点
要删除节点
$node->delete();
重要!如果删除的节点有子节点,则它们将附加到被删除节点的父节点。这种行为可能被更改。
重要!节点必须以模型的形式进行删除!不要尝试使用如下查询删除它们
Category::where('id', '=', $id)->delete();
这将破坏树结构!
SoftDeletes
特性也得到了支持,包括在模型级别。
您还可以删除所有子节点
$node->deleteWithChildren();
检索节点
在某些情况下,我们将使用一个 $id
变量,它是目标节点的 ID。
祖先和后代
祖先构成了从节点到其父节点的链。这对于显示当前类别的面包屑很有用。
后代是指子树中的所有节点,即节点的子节点、子节点的子节点等。
祖先和后代都可以进行预加载。
这些是关系
ancestors
:祖先关系descendantsNew
:后代关系children
:多对多关系parent
:属于关系
// Accessing ancestors $node->ancestors; // Accessing descendants $node->descendantsNew; // Accessing descendants $node->children;
父节点
获取父节点
$node->parent;
父节点集合
$node->parents($level);
兄弟节点
兄弟节点是与同一父节点具有相同父节点的节点。
// Get all siblings of the node $collection = $node->siblings()->get(); // Get siblings which are before the node $collection = $node->prevSiblings()->get(); // Get siblings which are after the node $collection = $node->nextSiblings()->get(); // Get a sibling that is immediately before the node $prevNode = $node->prevSibling()->first(); // Get a sibling that is immediately after the node $nextNode = $node->nextSibling()->first();
$prevNode = $node->prev()->first(); $nextNode = $node->next()->first();
节点查询
模型助手
控制台树
Table::fromModel($root->refresh())->draw();
$collection = Structure::all(); Table::fromTree($collection->toTree()) ->hideLevel() ->setExtraColumns( [ 'title' => 'Label', $root->leftAttribute()->name() => 'Left', $root->rightAttribute()->name() => 'Right', $root->levelAttribute()->name() => 'Deep', ] ) ->draw($output);
Structure::all()->toOutput([],null,'...');
检查一致性
您可以检查树是否损坏(即存在某些结构错误)
$bool = Category::isBroken();
可以获取错误统计信息
$data = Category::countErrors();
它将返回一个包含以下键的数组
oddness
- 错误的lft
和rgt
值的节点数量duplicates
- 拥有相同的lft
或rgt
值的节点数量wrong_parent
- 拥有无效的parent_id
值的节点数量,该值不对应于lft
和rgt
值missing_parent
- 拥有parent_id
指向不存在节点的节点数量
修复树
从 v3.3.1 版本开始,现在可以修复树。
对于单个树
Node::fixTree();
对于多树
Node::fixMultiTree();