PHPackages                             djvibegga/yii2-nested-set-behavior - 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. djvibegga/yii2-nested-set-behavior

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

djvibegga/yii2-nested-set-behavior
==================================

This extension allows you to get functional for nested set trees.

1.0(11y ago)0472BSD-3-ClausePHP

Since Feb 6Pushed 11y ago3 watchersCompare

[ Source](https://github.com/djvibegga/yii2-nested-set-behavior)[ Packagist](https://packagist.org/packages/djvibegga/yii2-nested-set-behavior)[ Docs](https://github.com/djvibegga/yii2-nested-set-behavior)[ RSS](/packages/djvibegga-yii2-nested-set-behavior/feed)WikiDiscussions master Synced 1mo ago

READMEChangelog (2)Dependencies (1)Versions (3)Used By (0)

Nested Set behavior for Yii 2
=============================

[](#nested-set-behavior-for-yii-2)

This extension allows you to get functional for nested set trees.

Installation
------------

[](#installation)

The preferred way to install this extension is through [composer](http://getcomposer.org/download/).

Either run

```
php composer.phar require djvibegga/yii2-nested-set-behavior "*"
```

or add

```
"djvibegga/yii2-nested-set-behavior": "*"
```

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

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

[](#configuring)

First you need to configure model as follows:

```
class Category extends ActiveRecord
{
	public function behaviors() {
		return [
			[
				'class' => NestedSet::className(),
			],
		];
	}

	public static function createQuery()
	{
		return new CategoryQuery(['modelClass' => get_called_class()]);
	}
}
```

Second you need to configure query model as follows:

```
class CategoryQuery extends ActiveQuery
{
	public function behaviors() {
		return [
			[
				'class' => NestedSetQuery::className(),
			],
		];
	}
}
```

There is no need to validate fields specified in `leftAttribute`, `rightAttribute`, `rootAttribute` and `levelAttribute` options. Moreover, there could be problems if there are validation rules for these. Please check if there are no rules for fields mentioned in model's rules() method.

In case of storing a single tree per database, DB structure can be built with `schema/schema.sql`. If you're going to store multiple trees you'll need `schema/schema-many-roots.sql`.

By default `leftAttribute`, `rightAttribute` and `levelAttribute` values are matching field names in default DB schemas so you can skip configuring these.

There are two ways this behavior can work: one tree per table and multiple trees per table. The mode is selected based on the value of `hasManyRoots` option that is `false` by default meaning single tree mode. In multiple trees mode you can set `rootAttribute` option to match existing field in the table storing the tree.

Selecting from a tree
---------------------

[](#selecting-from-a-tree)

In the following we'll use an example model `Category` with the following in its DB:

```
- 1. Mobile phones
	- 2. iPhone
	- 3. Samsung
		- 4. X100
		- 5. C200
	- 6. Motorola
- 7. Cars
	- 8. Audi
	- 9. Ford
	- 10. Mercedes

```

In this example we have two trees. Tree roots are ones with ID=1 and ID=7.

### Getting all roots

[](#getting-all-roots)

Using `NestedSet::roots()`:

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

Result:

Array of Active Record objects corresponding to Mobile phones and Cars nodes.

### Getting all descendants of a node

[](#getting-all-descendants-of-a-node)

Using `NestedSet::descendants()`:

```
$category = Category::find(1);
$descendants = $category->descendants()->all();
```

Result:

Array of Active Record objects corresponding to iPhone, Samsung, X100, C200 and Motorola.

### Getting all children of a node

[](#getting-all-children-of-a-node)

Using `NestedSet::children()`:

```
$category = Category::find(1);
$descendants = $category->children()->all();
```

Result:

Array of Active Record objects corresponding to iPhone, Samsung and Motorola.

### Getting all ancestors of a node

[](#getting-all-ancestors-of-a-node)

Using `NestedSet::ancestors()`:

```
$category = Category::find(5);
$ancestors = $category->ancestors()->all();
```

Result:

Array of Active Record objects corresponding to Samsung and Mobile phones.

### Getting parent of a node

[](#getting-parent-of-a-node)

Using `NestedSet::parent()`:

```
$category = Category::find(9);
$parent = $category->parent()->one();
```

Result:

Array of Active Record objects corresponding to Cars.

### Getting node siblings

[](#getting-node-siblings)

Using `NestedSet::prev()` or `NestedSet::next()`:

```
$category = Category::find(9);
$nextSibling = $category->next()->one();
```

Result:

Array of Active Record objects corresponding to Mercedes.

### Getting the whole tree

[](#getting-the-whole-tree)

You can get the whole tree using standard AR methods like the following.

For single tree per table:

```
Category::find()->addOrderBy('lft')->all();
```

For multiple trees per table:

```
Category::find()->where('root = ?', [$root_id])->addOrderBy('lft')->all();
```

Modifying a tree
----------------

[](#modifying-a-tree)

In this section we'll build a tree like the one used in the previous section.

### Creating root nodes

[](#creating-root-nodes)

You can create a root node using `NestedSet::saveNode()`. In a single tree per table mode you can create only one root node. If you'll attempt to create more there will be CException thrown.

```
$root = new Category;
$root->title = 'Mobile Phones';
$root->saveNode();
$root = new Category;
$root->title = 'Cars';
$root->saveNode();
```

Result:

```
- 1. Mobile Phones
- 2. Cars

```

### Adding child nodes

[](#adding-child-nodes)

There are multiple methods allowing you adding child nodes. To get more info about these refer to API. Let's use these to add nodes to the tree we have:

```
$category1 = new Category;
$category1->title = 'Ford';
$category2 = new Category;
$category2->title = 'Mercedes';
$category3 = new Category;
$category3->title = 'Audi';
$root = Category::find(1);
$category1->appendTo($root);
$category2->insertAfter($category1);
$category3->insertBefore($category1);
```

Result:

```
- 1. Mobile phones
	- 3. Audi
	- 4. Ford
	- 5. Mercedes
- 2. Cars

```

Logically the tree above doesn't looks correct. We'll fix it later.

```
$category1 = new Category;
$category1->title = 'Samsung';
$category2 = new Category;
$category2->title = 'Motorola';
$category3 = new Category;
$category3->title = 'iPhone';
$root = Category::find(2);
$category1->appendTo($root);
$category2->insertAfter($category1);
$category3->prependTo($root);
```

Result:

```
- 1. Mobile phones
	- 3. Audi
	- 4. Ford
	- 5. Mercedes
- 2. Cars
	- 6. iPhone
	- 7. Samsung
	- 8. Motorola

```

```
$category1 = new Category;
$category1->title = 'X100';
$category2 = new Category;
$category2->title = 'C200';
$node = Category::find(3);
$category1->appendTo($node);
$category2->prependTo($node);
```

Result:

```
- 1. Mobile phones
	- 3. Audi
		- 9. С200
		- 10. X100
	- 4. Ford
	- 5. Mercedes
- 2. Cars
	- 6. iPhone
	- 7. Samsung
	- 8. Motorola

```

Modifying a tree
----------------

[](#modifying-a-tree-1)

In this section we'll finally make our tree logical.

### Tree modification methods

[](#tree-modification-methods)

There are several methods allowing you to modify a tree. To get more info about these refer to API.

Let's start:

```
// move phones to the proper place
$x100 = Category::find(10);
$c200 = Category::find(9);
$samsung = Category::find(7);
$x100->moveAsFirst($samsung);
$c200->moveBefore($x100);
// now move all Samsung phones branch
$mobile_phones = Category::find(1);
$samsung->moveAsFirst($mobile_phones);
// move the rest of phone models
$iphone = Category::find(6);
$iphone->moveAsFirst($mobile_phones);
$motorola = Category::find(8);
$motorola->moveAfter($samsung);
// move car models to appropriate place
$cars = Category::find(2);
$audi = Category::find(3);
$ford = Category::find(4);
$mercedes = Category::find(5);

foreach([$audi, $ford, $mercedes] as $category) {
	$category->moveAsLast($cars);
}
```

Result:

```
- 1. Mobile phones
	- 6. iPhone
	- 7. Samsung
		- 10. X100
		- 9. С200
	- 8. Motorola
- 2. Cars
	- 3. Audi
	- 4. Ford
	- 5. Mercedes

```

### Moving a node making it a new root

[](#moving-a-node-making-it-a-new-root)

There is a special `moveAsRoot()` method that allows moving a node and making it a new root. All descendants are moved as well in this case.

Example:

```
$node = Category::find(10);
$node->moveAsRoot();
```

### Identifying node type

[](#identifying-node-type)

There are three methods to get node type: `isRoot()`, `isLeaf()`, `isDescendantOf()`.

Example:

```
$root = Category::find(1);
VarDumper::dump($root->isRoot()); //true;
VarDumper::dump($root->isLeaf()); //false;
$node = Category::find(9);
VarDumper::dump($node->isDescendantOf($root)); //true;
VarDumper::dump($node->isRoot()); //false;
VarDumper::dump($node->isLeaf()); //true;
$samsung = Category::find(7);
VarDumper::dump($node->isDescendantOf($samsung)); //true;
```

Useful code
-----------

[](#useful-code)

### Non-recursive tree traversal

[](#non-recursive-tree-traversal)

```
$categories = Category::find()->addOrderBy('lft')->all();
$level = 0;

foreach ($categories as $n => $category)
{
	if ($category->level == $level) {
		echo Html::endTag('li') . "\n";
	} elseif ($category->level > $level) {
		echo Html::beginTag('ul') . "\n";
	} else {
		echo Html::endTag('li') . "\n";

		for ($i = $level - $category->level; $i; $i--) {
			echo Html::endTag('ul') . "\n";
			echo Html::endTag('li') . "\n";
		}
	}

	echo Html::beginTag('li');
	echo Html::encode($category->title);
	$level = $category->level;
}

for ($i = $level; $i; $i--) {
	echo Html::endTag('li') . "\n";
	echo Html::endTag('ul') . "\n";
}
```

###  Health Score

28

—

LowBetter than 54% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity9

Limited adoption so far

Community10

Small or concentrated contributor base

Maturity64

Established project with proven stability

 Bus Factor1

Top contributor holds 100% 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 ~0 days

Total

2

Last Release

4113d ago

Major Versions

0.9.0 → 1.02015-02-06

### Community

Maintainers

![](https://www.gravatar.com/avatar/2e1cca9545f557cf570f82a7358700654fd3bcda4b48fd026146afe14ed03247?d=identicon)[djvibegga](/maintainers/djvibegga)

---

Top Contributors

[![djvibegga](https://avatars.githubusercontent.com/u/442165?v=4)](https://github.com/djvibegga "djvibegga (7 commits)")

---

Tags

extensionBehavioryiinested-set

### Embed Badge

![Health badge](/badges/djvibegga-yii2-nested-set-behavior/health.svg)

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

###  Alternatives

[yiiext/nested-set-behavior

AR models behavior that allows to work with nested sets tree.

158122.0k1](/packages/yiiext-nested-set-behavior)[sjaakp/yii2-taggable

Manage tags of ActiveRecord in Yii2.

3030.6k](/packages/sjaakp-yii2-taggable)[evgeniyrru/yii2-slick

Yii2 extension for Slick Carousel

22182.1k3](/packages/evgeniyrru-yii2-slick)[mdmsoft/yii2-autonumber

Auto number extension for the Yii framework

1830.9k](/packages/mdmsoft-yii2-autonumber)

PHPackages © 2026

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