sivanandaperumal/closure-table

为Laravel实现的邻接列表闭包表数据库设计模式,包括树的恢复

5.2.2 2019-02-11 09:43 UTC

README

Build Status Latest Stable Version Total Downloads

分支

  1. L4 支持 Laravel 4
  2. L5.1 支持 Laravel < 5.2
  3. L5.3 支持 Laravel 5.2-5.3
  4. L5.4 支持 Laravel 5.4
  5. 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 引擎。

这就是全部了,朋友们!已经为您创建了“虚拟”内容。您需要向实体迁移中添加一些字段,因为创建的“虚拟”内容仅包含 必需的 idparent_idposition实际深度

  1. id 是一个常规的自增列
  2. parent_id 列用于简化直接祖先查询,例如,简化构建整个树
  3. position 列被包广泛使用,用于使实体可排序
  4. 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_idpositionreal depth列名称:分别更改getParentIdColumn()getPositionColumn()getRealDepthColumn()的返回值
  4. 闭包表的ancestordescendantdepth列名称:分别更改getAncestorColumn()getDescendantColumn()getDepthColumn()的返回值。