PHPackages                             soda-framework/eloquent-closure - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Database &amp; ORM](/categories/database)
4. /
5. soda-framework/eloquent-closure

AbandonedLibrary[Database &amp; ORM](/categories/database)

soda-framework/eloquent-closure
===============================

Adjacency List’ed Closure Table database design pattern implementation for Laravel

0.2.1(9y ago)06.4k2MITPHPPHP &gt;=5.4.0

Since Feb 8Pushed 9y ago1 watchersCompare

[ Source](https://github.com/soda-framework/eloquent-closure)[ Packagist](https://packagist.org/packages/soda-framework/eloquent-closure)[ RSS](/packages/soda-framework-eloquent-closure/feed)WikiDiscussions master Synced 2mo ago

READMEChangelogDependencies (6)Versions (8)Used By (2)

ClosureTable
============

[](#closuretable)

[![Build Status](https://camo.githubusercontent.com/9dd45ea0ae98a0fc95e183233051b96eba1a13ccb91489a170a5c950ea52ecda/68747470733a2f2f7472617669732d63692e6f72672f6672616e7a6f73652f436c6f737572655461626c652e706e67)](https://travis-ci.org/franzose/ClosureTable)[![Latest Stable Version](https://camo.githubusercontent.com/fda3976c8effdde7a87afd790cb3065a9a89ee8947657b9b42d0ef9900ddfd27/68747470733a2f2f706f7365722e707567782e6f72672f6672616e7a6f73652f636c6f737572652d7461626c652f762f737461626c652e706e67)](https://packagist.org/packages/franzose/closure-table)[![Total Downloads](https://camo.githubusercontent.com/b13a23b89fb0d12ae6c1b77c5826c1e219ce5da833da46cf2563a6b87db554a6/68747470733a2f2f706f7365722e707567782e6f72672f6672616e7a6f73652f636c6f737572652d7461626c652f646f776e6c6f6164732e706e67)](https://packagist.org/packages/franzose/closure-table)

Branches
--------

[](#branches)

1. **L4** supports Laravel 4
2. **L5.1** supports Laravel &lt; 5.2
3. **master** is for any actual Laravel version

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
------------

[](#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
-----------------------

[](#setup-your-closuretable)

### Create models and migrations

[](#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](#customization)’ for more information.

Time of coding
--------------

[](#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)

[](#direct-ancestor-parent)

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

### Ancestors

[](#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)

[](#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

[](#descendants)

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

### Siblings

[](#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-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

[](#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

[](#moving)

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

### Deleting subtree

[](#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
-------------

[](#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.

###  Health Score

29

—

LowBetter than 60% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity18

Limited adoption so far

Community21

Small or concentrated contributor base

Maturity53

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 88.1% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~30 days

Total

4

Last Release

3290d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/0e55216f06906e0bdee2b78e0b2c0c9d5009a4bbab212341d04e3584ba8d864c?d=identicon)[ryzr](/maintainers/ryzr)

---

Top Contributors

[![franzose](https://avatars.githubusercontent.com/u/708158?v=4)](https://github.com/franzose "franzose (326 commits)")[![tomzx](https://avatars.githubusercontent.com/u/188960?v=4)](https://github.com/tomzx "tomzx (16 commits)")[![vjandrea](https://avatars.githubusercontent.com/u/1639757?v=4)](https://github.com/vjandrea "vjandrea (8 commits)")[![EspadaV8](https://avatars.githubusercontent.com/u/115825?v=4)](https://github.com/EspadaV8 "EspadaV8 (7 commits)")[![ryanbc](https://avatars.githubusercontent.com/u/12161815?v=4)](https://github.com/ryanbc "ryanbc (2 commits)")[![Noodlewitt](https://avatars.githubusercontent.com/u/1096831?v=4)](https://github.com/Noodlewitt "Noodlewitt (2 commits)")[![JaxxC](https://avatars.githubusercontent.com/u/4397007?v=4)](https://github.com/JaxxC "JaxxC (2 commits)")[![mahmoodbazdar](https://avatars.githubusercontent.com/u/5230489?v=4)](https://github.com/mahmoodbazdar "mahmoodbazdar (1 commits)")[![ashandi](https://avatars.githubusercontent.com/u/14348708?v=4)](https://github.com/ashandi "ashandi (1 commits)")[![ben-joostens](https://avatars.githubusercontent.com/u/1453735?v=4)](https://github.com/ben-joostens "ben-joostens (1 commits)")[![dominiczaq](https://avatars.githubusercontent.com/u/7099901?v=4)](https://github.com/dominiczaq "dominiczaq (1 commits)")[![aranw](https://avatars.githubusercontent.com/u/241565?v=4)](https://github.com/aranw "aranw (1 commits)")[![rafis](https://avatars.githubusercontent.com/u/3502395?v=4)](https://github.com/rafis "rafis (1 commits)")[![stevebauman](https://avatars.githubusercontent.com/u/6421846?v=4)](https://github.com/stevebauman "stevebauman (1 commits)")

---

Tags

laraveldatabasepagescategorieshierarchyclosure tableadjacency list

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/soda-framework-eloquent-closure/health.svg)

```
[![Health](https://phpackages.com/badges/soda-framework-eloquent-closure/health.svg)](https://phpackages.com/packages/soda-framework-eloquent-closure)
```

###  Alternatives

[franzose/closure-table

Adjacency List’ed Closure Table database design pattern implementation for Laravel

4641.1M3](/packages/franzose-closure-table)[kalnoy/nestedset

Nested Set Model for Laravel 5.7 and up

3.8k13.3M213](/packages/kalnoy-nestedset)[baril/bonsai

An implementation of the Closure Tables pattern for Eloquent.

3593.5k](/packages/baril-bonsai)[aimeos/laravel-nestedset

Nested Set Model for Laravel

292.0k2](/packages/aimeos-laravel-nestedset)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
