PHPackages                             paulzi/yii2-nested-sets - 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. [Utility &amp; Helpers](/categories/utility)
4. /
5. paulzi/yii2-nested-sets

ActiveYii2-extension[Utility &amp; Helpers](/categories/utility)

paulzi/yii2-nested-sets
=======================

Nested Sets Behavior for Yii2

v1.1.0(7y ago)85298.6k↓27.7%16[1 issues](https://github.com/paulzi/yii2-nested-sets/issues)9MITPHPPHP &gt;=5.4.0

Since Jul 23Pushed 7y ago11 watchersCompare

[ Source](https://github.com/paulzi/yii2-nested-sets)[ Packagist](https://packagist.org/packages/paulzi/yii2-nested-sets)[ RSS](/packages/paulzi-yii2-nested-sets/feed)WikiDiscussions master Synced 3d ago

READMEChangelogDependencies (3)Versions (11)Used By (9)

Yii2 Nested Sets Behavior
=========================

[](#yii2-nested-sets-behavior)

Implementation of nested sets algorithm for storing the trees in DB tables.

[![Packagist Version](https://camo.githubusercontent.com/a2021c564979b57f0ef89b356d132995aae57e2926af3629fad93e988f36580e/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f7061756c7a692f796969322d6e65737465642d736574732e737667)](https://packagist.org/packages/paulzi/yii2-nested-sets)[![Code Coverage](https://camo.githubusercontent.com/d7fa3f81d141e64e9fbfccd61ec1b5d1c86d0d13fa5d37002e22e393d6aaba5a/68747470733a2f2f696d672e736869656c64732e696f2f7363727574696e697a65722f636f7665726167652f672f7061756c7a692f796969322d6e65737465642d736574732f6d61737465722e737667)](https://scrutinizer-ci.com/g/paulzi/yii2-nested-sets/?branch=master)[![Build Status](https://camo.githubusercontent.com/6d04efb695e1461f8e284ad620d0b105325f8b11080358eada119d3056cbb6f5/68747470733a2f2f696d672e736869656c64732e696f2f7472617669732f7061756c7a692f796969322d6e65737465642d736574732f6d61737465722e737667)](https://travis-ci.org/paulzi/yii2-nested-sets)[![Total Downloads](https://camo.githubusercontent.com/e8076b6d7df649b00dba1c42fc8eda5878f764d0ed15b1aaefcca71476b65733/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f7061756c7a692f796969322d6e65737465642d736574732e737667)](https://packagist.org/packages/paulzi/yii2-nested-sets)

Install
-------

[](#install)

Install via Composer:

```
composer require paulzi/yii2-nested-sets
```

or add

```
"paulzi/yii2-nested-sets" : "^1.0"
```

to the `require` section of your `composer.json` file.

Migrations example
------------------

[](#migrations-example)

**Warning! `depth` attribute can not be unsigned!**

Single tree migration:

```
class m150722_150000_single_tree extends Migration
{
    public function up()
    {
        $tableOptions = null;
        if ($this->db->driverName === 'mysql') {
            // http://stackoverflow.com/questions/766809/whats-the-difference-between-utf8-general-ci-and-utf8-unicode-ci
            $tableOptions = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=InnoDB';
        }
        $this->createTable('{{%single_tree}}', [
            'id'    => Schema::TYPE_PK,
            'lft'   => Schema::TYPE_INTEGER . ' NOT NULL',
            'rgt'   => Schema::TYPE_INTEGER . ' NOT NULL',
            'depth' => Schema::TYPE_INTEGER . ' NOT NULL', // not unsigned!
            'name'  => Schema::TYPE_STRING . ' NOT NULL', // example field
        ], $tableOptions);
        $this->createIndex('lft', '{{%single_tree}}', ['lft', 'rgt']);
        $this->createIndex('rgt', '{{%single_tree}}', ['rgt']);
    }
}
```

Multiple tree migration:

```
class m150722_150100_multiple_tree extends Migration
{
    public function up()
    {
        $tableOptions = null;
        if ($this->db->driverName === 'mysql') {
            // http://stackoverflow.com/questions/766809/whats-the-difference-between-utf8-general-ci-and-utf8-unicode-ci
            $tableOptions = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=InnoDB';
        }
        $this->createTable('{{%multiple_tree}}', [
            'id'    => Schema::TYPE_PK,
            'tree'  => Schema::TYPE_INTEGER . ' NULL',
            'lft'   => Schema::TYPE_INTEGER . ' NOT NULL',
            'rgt'   => Schema::TYPE_INTEGER . ' NOT NULL',
            'depth' => Schema::TYPE_INTEGER . ' NOT NULL', // not unsigned!
            'name'  => Schema::TYPE_STRING . ' NOT NULL', // example field
        ], $tableOptions);
        $this->createIndex('lft', '{{%multiple_tree}}', ['tree', 'lft', 'rgt']);
        $this->createIndex('rgt', '{{%multiple_tree}}', ['tree', 'rgt']);
    }
}
```

Configuring
-----------

[](#configuring)

```
use paulzi\nestedsets\NestedSetsBehavior;

/**
 * @property integer $id
 * @property integer $lft
 * @property integer $rgt
 * @property integer $depth
 *
 * @mixin NestedSetsBehavior
 */

class Sample extends \yii\db\ActiveRecord
{
    public function behaviors() {
        return [
            [
                'class' => NestedSetsBehavior::class,
                // 'treeAttribute' => 'tree',
            ],
        ];
    }

    public function transactions()
    {
        return [
            self::SCENARIO_DEFAULT => self::OP_ALL,
        ];
    }
}
```

Optional you can setup Query for finding roots:

```
class Sample extends \yii\db\ActiveRecord
{
    public static function find()
    {
        return new SampleQuery(get_called_class());
    }
}
```

Query class:

```
use paulzi\nestedsets\NestedSetsQueryTrait;

class SampleQuery extends \yii\db\ActiveQuery
{
    use NestedSetsQueryTrait;
}
```

Options
-------

[](#options)

- `$treeAttribute = null` - setup tree attribute for multiple tree in table schema.
- `$leftAttribute = 'lft'` - left attribute in table schema.
- `$rightAttribute = 'rgt'` - right attribute in table schema.
- `$depthAttribute = 'depth'` - depth attribute in table schema (note: it must be signed int).

Usage
-----

[](#usage)

### Selection

[](#selection)

**Getting the root nodes**

If you connect `NestedSetsQueryTrait`, you can get all the root nodes:

```
$roots = Sample::find()->roots()->all();
```

**Getting ancestors of a node**

To get ancestors of a node:

```
$node11 = Sample::findOne(['name' => 'node 1.1']);
$parents = $node11->parents; // via relation
$parents = $node11->getParents()->all(); // via query
$parents = $node11->getParents(2)->all(); // get 2 levels of ancestors
```

To get parent of a node:

```
$node11 = Sample::findOne(['name' => 'node 1.1']);
$parent = $node11->parent; // via relation
$parent = $node11->getParent()->one(); // via query
```

To get root of a node:

```
$node11 = Sample::findOne(['name' => 'node 1.1']);
$root = $node11->root; // via relation
$root = $node11->getRoot()->one(); // via query
```

**Getting descendants of a node**

To get all the descendants of a node:

```
$node11 = Sample::findOne(['name' => 'node 1.1']);
$descendants = $node11->descendants; // via relation
$descendants = $node11->getDescendants()->all(); // via query
$descendants = $node11->getDescendants(2, true)->all(); // get 2 levels of descendants and self node
$descendants = $node11->getDescendants(3, false, true)->all(); // get 3 levels of descendants in back order
```

To populate `children` relations for self and descendants of a node:

```
$node11 = Sample::findOne(['name' => 'node 1.1']);
$tree = $node11->populateTree(); // populate all levels
$tree = $node11->populateTree(2); // populate 2 levels of descendants
```

To get the children of a node:

```
$node11 = Sample::findOne(['name' => 'node 1.1']);
$children = $node11->children; // via relation
$children = $node11->getChildren()->all(); // via query
```

**Getting the leaves nodes**

To get all the leaves of a node:

```
$node11 = Sample::findOne(['name' => 'node 1.1']);
$leaves = $node11->leaves; // via relation
$leaves = $node11->getLeaves(2)->all(); // get 2 levels of leaves via query
```

**Getting the neighbors nodes**

To get the next node:

```
$node11 = Sample::findOne(['name' => 'node 1.1']);
$next = $node11->next; // via relation
$next = $node11->getNext()->one(); // via query
```

To get the previous node:

```
$node11 = Sample::findOne(['name' => 'node 1.1']);
$prev = $node11->prev; // via relation
$prev = $node11->getPrev()->one(); // via query
```

### Some checks

[](#some-checks)

```
$node1 = Sample::findOne(['name' => 'node 1']);
$node11 = Sample::findOne(['name' => 'node 1.1']);
$node11->isRoot() - return true, if node is root
$node11->isLeaf() - return true, if node is leaf
$node11->isChildOf($node1) - return true, if node11 is child of $node1
```

### Modifications

[](#modifications)

To make a root node:

```
$node11 = new Sample();
$node11->name = 'node 1.1';
$node11->makeRoot()->save();
```

*Note: if you allow multiple trees and attribute `tree` is not set, it automatically takes the primary key value.*

To prepend a node as the first child of another node:

```
$node1 = Sample::findOne(['name' => 'node 1']);
$node11 = new Sample();
$node11->name = 'node 1.1';
$node11->prependTo($node1)->save(); // inserting new node
```

To append a node as the last child of another node:

```
$node11 = Sample::findOne(['name' => 'node 1.1']);
$node12 = Sample::findOne(['name' => 'node 1.2']);
$node12->appendTo($node11)->save(); // move existing node
```

To insert a node before another node:

```
$node13 = Sample::findOne(['name' => 'node 1.3']);
$node12 = new Sample();
$node12->name = 'node 1.2';
$node12->insertBefore($node13)->save(); // inserting new node
```

To insert a node after another node:

```
$node13 = Sample::findOne(['name' => 'node 1.3']);
$node14 = Sample::findOne(['name' => 'node 1.4']);
$node14->insertAfter($node13)->save(); // move existing node
```

To delete a node with descendants:

```
$node11 = Sample::findOne(['name' => 'node 1.1']);
$node11->delete(); // delete node, children come up to the parent
$node11->deleteWithChildren(); // delete node and all descendants
```

###  Health Score

43

—

FairBetter than 89% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity49

Moderate usage in the ecosystem

Community26

Small or concentrated contributor base

Maturity64

Established project with proven stability

 Bus Factor1

Top contributor holds 87.5% 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 ~122 days

Recently: every ~164 days

Total

10

Last Release

2895d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/9402208?v=4)[PaulZi](/maintainers/PaulZi)[@paulzi](https://github.com/paulzi)

---

Top Contributors

[![paulzi](https://avatars.githubusercontent.com/u/9402208?v=4)](https://github.com/paulzi "paulzi (21 commits)")[![defk](https://avatars.githubusercontent.com/u/435460?v=4)](https://github.com/defk "defk (3 commits)")

---

Tags

yii2nested sets

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/paulzi-yii2-nested-sets/health.svg)

```
[![Health](https://phpackages.com/badges/paulzi-yii2-nested-sets/health.svg)](https://phpackages.com/packages/paulzi-yii2-nested-sets)
```

###  Alternatives

[craftcms/cms

Craft CMS

3.6k3.6M3.1k](/packages/craftcms-cms)[paulzi/yii2-auto-tree

Allow apply multiple tree behavior for ActiveRecord in Yii2

5055.7k8](/packages/paulzi-yii2-auto-tree)[paulzi/yii2-nested-intervals

Nested Intervals Behavior for Yii2

3063.3k4](/packages/paulzi-yii2-nested-intervals)[devgroup/yii2-jstree-widget

jsTree widget for yii2

2323.7k](/packages/devgroup-yii2-jstree-widget)

PHPackages © 2026

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