ClosureTable

邻列表的闭包表Laravel框架的数据库设计模式实现。(Adjacency List’ed Closure Table database design pattern implementation for Laravel framework.)

Github星跟蹤圖

ClosureTable

分支

  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的数据库包。它旨在用于需要在数据库中操作分层数据的情况。该包是一个名为Closure Table的着名数据库设计模式的实现。该软件包包括用于模型和迁移的生成器。

安装

要安装软件包,请将以下内容放入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 :entity class name;如果使用名称空间名称,那么默认的闭包类名称将以该名称空间
  3. 为前缀
  4. --entity-table , -et [可选] :实体表名称
  5. --closure , -c [可选] :封闭类名称
  6. --closure-table [可选] , -ct :封闭表名称
  7. --models-path , -mdl [可选] :自定义模型路径
  8. --migrations-path , -mgr [可选] :自定义迁移路径
  9. --use-innodb 和 -i [可选] :创建InnoDB迁移也是可选的。设置这将启用InnoDB引擎。

这几乎都是,伙计们! 'dummy'的东西刚刚为你创造。您需要为实体迁移添加一些字段,因为创建的“dummy”仅包含 必填id , parent_id , position 和真实深度列:

  1. id 是一个常规的自动增量列
  2. parent_id 列用于简化祖先查询的立即操作,例如简化构建整个树
  3. position 列被封装广泛用于实体可排序
  4. 真实深度 列也用于简化查询并减少查询次数

默认情况下,实体的闭包表包含以下列:

  1. Autoincremented identifier
  2. Ancestor column指向父节点
  3. Descendant column指向子节点
  4. Depth column在树中显示节点深度

它是通过闭合表格模式设计的,所以请记住,您不能删除这四列。

请记住,许多内容都是可自定义的,所以请参阅自定义以获取更多信息。

编码时间

一旦你的模型和数据库表被创建,最后,你可以开始真正的编码。在这里,我将向您展示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);

您处理集合,因此您可以像平常一样控制其项目。 Descendants? 它们已经装好了。

 $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

定制

您可以自定义由ClosureTableartisan 命令创建的类中的默认内容:

  1. Entity table name:更改受保护的$table 属性。
  2. Closure table name:在您的 ClosureTable(例如 PageClosure)中执行相同的操作。
  3. Entity's parent_id, position, and real depth column names:分别更改 getParentIdColumn()、getPositionColumn()和getRealDepthColumn()的返回值。
  4. Closure table's ancestor, descendant, and depth columns names:分别更改 getAncestorColumn()、getDescendantColumn()和 getDepthColumn() 的返回值。

主要指標

概覽
名稱與所有者franzose/ClosureTable
主編程語言PHP
編程語言PHP (語言數: 1)
平台BSD, Linux, Mac, Windows
許可證MIT License
所有者活动
創建於2013-03-04 10:39:39
推送於2023-08-19 17:13:45
最后一次提交2020-10-06 08:22:48
發布數28
最新版本名稱v6.1.1 (發布於 )
第一版名稱v1.0 (發布於 )
用户参与
星數463
關注者數29
派生數107
提交數527
已啟用問題?
問題數138
打開的問題數15
拉請求數102
打開的拉請求數5
關閉的拉請求數12
项目设置
已啟用Wiki?
已存檔?
是復刻?
已鎖定?
是鏡像?
是私有?

ClosureTable

Build Status
Latest Stable Version
Total Downloads

Branches

  1. L4 supports Laravel 4
  2. L5.1 supports Laravel < 5.2
  3. L5.3 supports Laravel 5.2-5.3
  4. L5.4 supports Laravel 5.4
  5. master is for any actual Laravel version, so be careful

Hi, this is a database package for Laravel. It's intended to use when you need to operate hierarchical data in database. The package is an implementation of a well-known database design pattern called Closure Table. The package includes generators for models and migrations.

Installation

To install the package, put the following in your composer.json:

"require": {
	"franzose/closure-table": "4.*"
}

And to app/config/app.php:

'providers' => array(
        // ...
        'Franzose\ClosureTable\ClosureTableServiceProvider',
    ),

Setup your ClosureTable

Create models and migrations

For example, let's assume you're working on pages. You can just use an artisan command to create models and migrations automatically without preparing all the stuff by hand. Open terminal and put the following:

php artisan closuretable:make --entity=page

All options of the command:

  1. --namespace, -ns [optional]: namespace for classes, set by --entity and --closure options, helps to avoid namespace duplication in those options
  2. --entity, -e: entity class name; if namespaced name is used, then the default closure class name will be prepended with that namespace
  3. --entity-table, -et [optional]: entity table name
  4. --closure, -c [optional]: closure class name
  5. --closure-table [optional], -ct: closure table name
  6. --models-path, -mdl [optional]: custom models path
  7. --migrations-path, -mgr [optional]: custom migrations path
  8. --use-innodb and -i [optional]: InnoDB migrations have been made optional as well with new paramaters. Setting this will enable the InnoDB engine.

That's almost all, folks! The ‘dummy’ stuff has just been created for you. You will need to add some fields to your entity migration because the created ‘dummy’ includes just required id, parent_id, position, and real depth columns:

  1. id is a regular autoincremented column
  2. parent_id column is used to simplify immediate ancestor querying and, for example, to simplify building the whole tree
  3. position column is used widely by the package to make entities sortable
  4. real depth column is also used to simplify queries and reduce their number

By default, entity’s closure table includes the following columns:

  1. Autoincremented identifier
  2. Ancestor column points on a parent node
  3. Descendant column points on a child node
  4. Depth column shows a node depth in the tree

It is by closure table pattern design, so remember that you must not delete these four columns.

Remember that many things are made customizable, so see ‘Customization’ for more information.

Time of coding

Once your models and their database tables are created, at last, you can start actually coding. Here I will show you ClosureTable's specific approaches.

Direct ancestor (parent)

$parent = Page::find(15)->getParent();

Ancestors

$page = Page::find(15);
$ancestors = $page->getAncestors();
$ancestors = $page->getAncestorsTree(); // Tree structure
$ancestors = $page->getAncestorsWhere('position', '=', 1);
$hasAncestors = $page->hasAncestors();
$ancestorsNumber = $page->countAncestors();

Direct descendants (children)

$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

Descendants

$page = Page::find(15);
$descendants = $page->getDescendants();
$descendants = $page->getDescendantsWhere('position', '=', 1);
$descendantsTree = $page->getDescendantsTree();
$hasDescendants = $page->hasDescendants();
$descendantsNumber = $page->countDescendants();

Siblings

$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 (entities that have no ancestors)

$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

Entire tree

$tree = Page::getTree();
$treeByCondition = Page::getTreeWhere('position', '>=', 1);

You deal with the collection, thus you can control its items as you usually do. Descendants? They are already loaded.

$tree = Page::getTree();
$page = $tree->find(15);
$children = $page->getChildren();
$child = $page->getChildAt(3);
$grandchildren = $page->getChildAt(3)->getChildren(); //and so on

Moving

$page = Page::find(25);
$page->moveTo(0, Page::find(14));
$page->moveTo(0, 14);

Deleting subtree

If you don't use foreign keys for some reason, you can delete subtree manually. This will delete the page and all its descendants:

$page = Page::find(34);
$page->deleteSubtree();
$page->deleteSubtree(true); //with subtree ancestor
$page->deleteSubtree(false, true); //without subtree ancestor and force delete

Customization

You can customize default things in your own classes created by the ClosureTable artisan command:

  1. Entity table name: change protected $table property
  2. Closure table name: do the same in your ClosureTable (e.g. PageClosure)
  3. Entity's parent_id, position, and real depth column names: change return values of getParentIdColumn(), getPositionColumn(), and getRealDepthColumn() respectively
  4. Closure table's ancestor, descendant, and depth columns names: change return values of getAncestorColumn(), getDescendantColumn(), and getDepthColumn() respectively.