You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
PaulZi 1d50bd6b45 Add unit tests 9 years ago
sample-migrations First commit 9 years ago
tests Add unit tests 9 years ago
.gitignore First commit 9 years ago
.scrutinizer.yml Add unit tests 9 years ago
.travis.yml Add unit tests 9 years ago
LICENSE First commit 9 years ago
NestedSetsBehavior.php Fix stupid bug in moveNode() 9 years ago
NestedSetsQueryTrait.php Clean usage of Trait variant 9 years ago
README.md Add unit tests 9 years ago
composer.json Add unit tests 9 years ago
phpunit.xml.dist Add unit tests 9 years ago

README.md

Yii2 Nested Sets Behavior

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

Packagist Version Code Coverage Build Status Total Downloads

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

Sample migrations are in the folder sample-migrations:

  • m150722_150000_single_tree.php - for single tree tables;
  • m150722_150100_multiple_tree.php - for multiple tree tables.

Configuring

use paulzi\nestedsets\NestedSetsBehavior;

class Sample extends \yii\db\ActiveRecord
{
    public function behaviors() {
        return [
            [
                'class' => NestedSetsBehavior::className(),
                // '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

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

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

$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

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