sodacms / closure-table
Laravel 的邻接表闭包表数据库设计模式实现
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
- 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
命令的所有选项
1. --namespace
,-ns
[可选]:类命名空间;由 --entity
和 --closure
选项设置,有助于避免在那些选项中发生命名空间重复
2. --entity
,-e
:[可选]:实体类名称;如果使用了命名空间名称,则默认闭包类名称将添加该命名空间
3. --entity-table
,-et
[可选]:实体表名称
4. --closure
,-c
[可选]:闭包类名称
5. --closure-table
[可选],-ct
:[可选]:闭包表名称
6. --models-path
,-mdl
[可选]:自定义模型路径
7. --migrations-path
,-mgr
[可选]:自定义迁移路径
8. --use-innodb
和 -i
[可选]:由于新参数,InnoDB 迁移也已设置为可选。设置此选项将启用 InnoDB 引擎。
这就差不多完成了,大家!已经为您创建了“虚拟”内容。您需要将一些字段添加到实体迁移中,因为创建的“虚拟”只包括 必需的 id
、parent_id
、position
和 real depth
列
id
是一个常规的自增列parent_id
列用于简化直接祖先查询,例如,简化构建整个树position
列被包广泛使用,以使实体可排序real depth
列也用于简化查询并减少它们的数量
默认情况下,实体的闭包表包括以下列
1. 自增标识符
2. 祖先列 指向父节点
3. 后代列 指向子节点
4. 深度列 显示树中节点深度
这是由闭包表模式设计决定的,所以请记住,您绝对不能删除这四列。
请记住,许多内容都可以自定义,所以请查看“自定义”以获取更多信息。
编码时间
一旦您的模型及其数据库表创建完成,最后,您就可以开始实际编码了。在这里,我将向您展示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
命令创建的类来自定义默认项。
1. 实体表名:更改protected $table
属性
2. 闭包表名:在您的ClosureTable
(例如PageClosure
)中做同样的事情
3. 实体的parent_id
、position
和real depth
列名:分别更改getParentIdColumn()
、getPositionColumn()
和getRealDepthColumn()
的返回值
4. 闭包表的ancestor
、descendant
和depth
列名:分别更改getAncestorColumn()
、getDescendantColumn()
和getDepthColumn()
的返回值。