sivanandaperumal / closure-table
为Laravel实现的邻接列表闭包表数据库设计模式,包括树的恢复
5.2.2
2019-02-11 09:43 UTC
Requires
- php: >=5.4.0
Requires (Dev)
- doctrine/dbal: dev-master
- mockery/mockery: dev-master
- orchestra/testbench: dev-master
- phpunit/phpunit: dev-master
- way/laravel-test-helpers: dev-master
- way/phpunit-wrappers: dev-master
README
分支
- L4 支持 Laravel 4
- L5.1 支持 Laravel < 5.2
- L5.3 支持 Laravel 5.2-5.3
- L5.4 支持 Laravel 5.4
- master 适用于任何实际的 Laravel 版本,所以请小心
嗨,这是一个用于 Laravel 的数据库包。当您需要在数据库中操作层次数据时,请使用此包。该包是实现称为闭包表的知名数据库设计模式的一个实现。该包包括模型和迁移的生成器。
安装
要安装此包,请将以下内容放入您的 composer.json 中
"require": { "franzose/closure-table": "4.*" }
并将其添加到 app/config/app.php
'providers' => array( // ... 'Franzose\ClosureTable\ClosureTableServiceProvider', ),
设置您的 ClosureTable
创建模型和迁移
例如,假设您正在处理页面。您可以使用 artisan
命令自动创建模型和迁移,而无需手动准备所有内容。打开终端并输入以下内容
php artisan closuretable:make --entity=page
命令的所有选项
--namespace
,-ns
[可选]:类的命名空间,由--entity
和--closure
选项设置,有助于避免这些选项中的命名空间重复--entity
,-e
:实体类名;如果使用了命名空间名称,则默认的闭包类名称将带有该命名空间前缀--entity-table
,-et
[可选]:实体表名称--closure
,-c
[可选]:闭包类名称--closure-table
[可选],-ct
:闭包表名称--models-path
,-mdl
[可选]:自定义模型路径--migrations-path
,-mgr
[可选]:自定义迁移路径--use-innodb
和-i
[可选]:新参数使 InnoDB 迁移也变为可选。设置此参数将启用 InnoDB 引擎。
这就是全部了,朋友们!已经为您创建了“虚拟”内容。您需要向实体迁移中添加一些字段,因为创建的“虚拟”内容仅包含 必需的 id
、parent_id
、position
和 实际深度
列
id
是一个常规的自增列parent_id
列用于简化直接祖先查询,例如,简化构建整个树position
列被包广泛使用,用于使实体可排序real depth
列也用于简化查询并减少其数量
默认情况下,实体闭包表包含以下列
- 自增标识符
- 祖先列 指向父节点
- 后代列 指向子节点
- 深度列 显示节点在树中的深度
这是闭包表模式设计,所以请记住,您绝对不能删除这四列。
请记住,许多内容都是可定制的,因此请参阅“自定义”以获取更多信息。
编码时间
一旦创建了您的模型及其数据库表,最后,您可以开始实际编码。在这里,我将向您展示 ClosureTable 的具体方法。
直接祖先(父节点)
$parent = Page::find(15)->getParent();
祖先
$page = Page::find(15); $ancestors = $page->getAncestors(); $ancestors = $page->getAncestorsTree(); // Tree structure $ancestors = $page->getAncestorsWhere('position', '=', 1); $hasAncestors = $page->hasAncestors(); $ancestorsNumber = $page->countAncestors();
直接后代(子节点)
$page = Page::find(15); $children = $page->getChildren(); $hasChildren = $page->hasChildren(); $childrenNumber = $page->countChildren(); $newChild = new Page(array( 'title' => 'The title', 'excerpt' => 'The excerpt', 'content' => 'The content of a child' )); $newChild2 = new Page(array( 'title' => 'The title', 'excerpt' => 'The excerpt', 'content' => 'The content of a child' )); $page->addChild($newChild); //you can set child position $page->addChild($newChild, 5); //you can get the child $child = $page->addChild($newChild, null, true); $page->addChildren([$newChild, $newChild2]); $page->getChildAt(5); $page->getFirstChild(); $page->getLastChild(); $page->getChildrenRange(0, 2); $page->removeChild(0); $page->removeChild(0, true); //force delete $page->removeChildren(0, 3); $page->removeChildren(0, 3, true); //force delete
后代
$page = Page::find(15); $descendants = $page->getDescendants(); $descendants = $page->getDescendantsWhere('position', '=', 1); $descendantsTree = $page->getDescendantsTree(); $hasDescendants = $page->hasDescendants(); $descendantsNumber = $page->countDescendants();
兄弟
$page = Page::find(15); $first = $page->getFirstSibling(); //or $page->getSiblingAt(0); $last = $page->getLastSibling(); $atpos = $page->getSiblingAt(5); $prevOne = $page->getPrevSibling(); $prevAll = $page->getPrevSiblings(); $hasPrevs = $page->hasPrevSiblings(); $prevsNumber = $page->countPrevSiblings(); $nextOne = $page->getNextSibling(); $nextAll = $page->getNextSiblings(); $hasNext = $page->hasNextSiblings(); $nextNumber = $page->countNextSiblings(); //in both directions $hasSiblings = $page->hasSiblings(); $siblingsNumber = $page->countSiblings(); $sibligns = $page->getSiblingsRange(0, 2); $page->addSibling(new Page); $page->addSibling(new Page, 3); //third position //add and get the sibling $sibling = $page->addSibling(new Page, null, true); $page->addSiblings([new Page, new Page]); $page->addSiblings([new Page, new Page], 5); //insert from fifth position
根(没有祖先的实体)
$roots = Page::getRoots(); $isRoot = Page::find(23)->isRoot(); Page::find(11)->makeRoot(0); //at the moment we always have to set a position when making node a root
整个树
$tree = Page::getTree(); $treeByCondition = Page::getTreeWhere('position', '>=', 1);
您处理的是集合,因此您可以像通常那样控制其项。后代?它们已经加载了。
$tree = Page::getTree(); $page = $tree->find(15); $children = $page->getChildren(); $child = $page->getChildAt(3); $grandchildren = $page->getChildAt(3)->getChildren(); //and so on
移动
$page = Page::find(25); $page->moveTo(0, Page::find(14)); $page->moveTo(0, 14);
删除子树
由于某些原因不使用外键时,您可以手动删除子树。这将删除页面及其所有子节点
$page = Page::find(34); $page->deleteSubtree(); $page->deleteSubtree(true); //with subtree ancestor $page->deleteSubtree(false, true); //without subtree ancestor and force delete
自定义
您可以使用ClosureTable artisan
命令创建的类来自定义默认设置
- 实体表名称:更改
protected $table
属性 - 闭包表名称:在您的
ClosureTable
(例如PageClosure
)中做同样的操作 - 实体的
parent_id
、position
和real depth
列名称:分别更改getParentIdColumn()
、getPositionColumn()
和getRealDepthColumn()
的返回值 - 闭包表的
ancestor
、descendant
和depth
列名称:分别更改getAncestorColumn()
、getDescendantColumn()
和getDepthColumn()
的返回值。