efureev / laravel-trees
Laravel的多树结构
Requires
- php: ^8.2|^8.3
- efureev/support: ^4.27
- illuminate/database: ^11.0
- illuminate/events: ^11.0
Requires (Dev)
- fakerphp/faker: ^1.23
- orchestra/testbench: ^v9.0
- phpunit/phpunit: ^11.0
- squizlabs/php_codesniffer: ^3.9
- symfony/var-dumper: ^v7.0
- dev-master
- v5.x-dev
- v5.0.0
- v5.0.0-rc2
- v5.0.0-rc1
- v4.0.0
- v3.8.4
- v3.8.3
- v3.8.2
- v3.8.1
- v3.8.0
- v3.7.2
- 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-lara11
- dev-next
- dev-php7
This package is auto-updated.
Last update: 2024-09-06 20:58:01 UTC
README
内容
信息
本包是多树结构(许多根节点)。
嵌套集是什么?
嵌套集或嵌套集模型是一种有效存储关系型表中的层次数据的方法。来自维基百科
嵌套集模型是按照树遍历对节点进行编号,每个节点访问两次,按访问顺序分配数字,并在两次访问中。这为每个节点留下两个数字,作为两个属性存储。查询变得便宜:可以通过比较这些数字来测试层次结构成员资格。更新需要重新编号,因此代价昂贵。
应用
当树很少更新时,NSM表现良好。它被调整以快速获取相关节点。它非常适合构建多深度菜单或商店的分类。
要求
- PHP: 8.2|8.3
- Laravel: ^11.*
强烈建议使用支持事务的数据库(如MySQL的InnoDB、PostgreSQL)来确保树免受可能损坏的影响。
安装
要安装此包,请在终端中
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();