PHPackages                             nlzet/doctrine2-nestedset - 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. nlzet/doctrine2-nestedset

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

nlzet/doctrine2-nestedset
=========================

This Doctrine2 nested set extension.

1.0.1(9y ago)13.6k↓100%1LGPLPHPPHP &gt;=5.3.3

Since Sep 7Pushed 8y ago1 watchersCompare

[ Source](https://github.com/nlzet/doctrine2-nestedset)[ Packagist](https://packagist.org/packages/nlzet/doctrine2-nestedset)[ RSS](/packages/nlzet-doctrine2-nestedset/feed)WikiDiscussions master Synced today

READMEChangelog (2)DependenciesVersions (3)Used By (0)

Doctrine2 NestedSet
===================

[](#doctrine2-nestedset)

This Doctrine2 extension implements the nested set model (modified pre-order tree traversal algorithm) for Doctrine2. This allows storing hierarchical data, a collection of data where each item has a single parent and zero or more children, in the flat tables of a relational database. For more information on the nested set model, see:

[https://en.wikipedia.org/wiki/Nested\_set\_model](https://en.wikipedia.org/wiki/Nested_set_model)

Introduction
------------

[](#introduction)

Nested Set is a solution for storing hierarchical data that provides very fast read access. However, updating nested set trees is more costly. Therefore this solution is best suited for hierarchies that are much more frequently read than written to. And because of the nature of the web, this is the case for most web applications.

Setting Up
----------

[](#setting-up)

To set up your model as a Nested Set, your entity classes must implement the `DoctrineExtensions\NestedSet\Node` interface. Each entity class must contain mapped fields for holding the Nested Set left and right values.

Here's an example using annotation mapping:

```
namespace Entity;

use DoctrineExtensions\NestedSet\Node;

/**
 * @Entity
 */
class Category implements Node
{
    /**
     * @Id @Column(type="integer")
     * @GeneratedValue
     */
    private $id;

    /**
     * @Column(type="integer")
     */
    private $lft;

    /**
     * @Column(type="integer")
     */
    private $rgt;

    /**
     * @Column(type="string", length="16")
     */
    private $name;

    public function getId() { return $this->id; }

    public function getLeftValue() { return $this->lft; }
    public function setLeftValue($lft) { $this->lft = $lft; }

    public function getRightValue() { return $this->rgt; }
    public function setRightValue($rgt) { $this->rgt = $rgt; }

    public function getName() { return $this->name; }
    public function setName($name) { $this->name = $name; }

    public function __toString() { return $this->name; }
}

```

Generally you do not need to, and should not, interact with the left and right fields. These are used internally to manage the tree structure.

Multiple Trees
--------------

[](#multiple-trees)

The nested set implementation can be configured to allow your table to have multiple root nodes, and therefore multiple trees within the same table. This is done by implementing the `DoctrineExtensions\NestedSet\MultipleRootNode`interface (instead of `DoctrineExtensions\NestedSet\Node`) and mapping a root field.

Extending our annotation example:

```
/**
 * @Column(type="integer")
 */
private $root;

public function getRootValue() { return $this->root; }
public function setRootValue($root) { $this->root = $root; }

```

Like the left and right fields, you generally do not need to interact with the root value.

Working with Trees
------------------

[](#working-with-trees)

After you successfully set up your model as a nested set you can start working with it. Working with Doctrine2's nested set implementation is all about two classes: Manager and NodeWrapper. NodeWrapper wraps your entity classes giving you access to the underlying tree structure. Manager provides methods for creating new trees and fetching existing trees.

To fetch an entire tree from the database:

```
$config = new Config($em, 'Entity\Category');
$nsm = new Manager($config);
$rootNode = $nsm->fetchTree(1);

```

In this example, `$rootNode` is an instance of `NodeWrapper` wrapping your model's root node. To get access to your model object:

```
$modelObject = $rootNode->getNode();

```

### Creating a Root Node

[](#creating-a-root-node)

```
$config = new Config($em, 'Entity\Category');
$nsm = new Manager($config);

$category = new Category();
$category->setName('Root Category 1');

$rootNode = $nsm->createRoot($category);

```

### Inserting a Node

[](#inserting-a-node)

```
$child1 = new Category();
$child1->setName('Child Category 1');

$child2 = new Category();
$child2->setName('Child Category 2');

$rootNode->addChild($child1);
$rootNode->addChild($child2);

```

### Deleting a Node

[](#deleting-a-node)

You must always delete a node using the `NodeWrapper::delete()` method instead of EntityManager's delete method. `NodeWrapper::delete()` takes care of updating the tree when deleting nodes:

```
$category = $em->getRepository('Entity\Category')->findOneByName('Child Category 1');
$node = $nsm->wrapNode($category);
$node->delete();

```

Deleting a node will also delete all descendants of that node. So make sure you move them elsewhere before you delete the node if you don't want to delete them.

### Moving a Node

[](#moving-a-node)

Moving a node is simple. NodeWrapper offers several methods for moving nodes around between trees:

- moveAsLastChildOf($other)
- moveAsFirstChildOf($other)
- moveAsPrevSiblingOf($other)
- moveAsNextSiblingOf($other)

### Examining a Node

[](#examining-a-node)

You can examine the nodes and what type of node they are by using some of the following functions:

```
$isLeaf = $node->isLeaf();
$isRoot = $node->isRoot();

```

### Examining and Retrieving Siblings

[](#examining-and-retrieving-siblings)

You can easily check if a node has any next or previous siblings by using the following methods:

```
$hasNextSib = $node->hasNextSibling();
$hasPrevSib = $node->hasPrevSibling();

```

You can also retrieve the next or previous siblings if they exist with the following methods:

```
$nextSib = $node->getNextSibling();
$prevSib = $node->getPrevSibling();

```

If you want to retrieve an array of all the siblings you can simply use the `getSiblings()` method:

```
$siblings = $node->getSiblings();

```

### Examining and Retrieving Descendants

[](#examining-and-retrieving-descendants)

You can check if a node has a parent or children by using the following methods:

```
$hasChildren = $node->hasChildren();
$hasParent = $node->hasParent();

```

You can retrieve a nodes first and last child by using the following methods:

```
$firstChild = $node->getFirstChild();
$lastChild = $node->getLastChild();

```

Or if you want to retrieve the parent of a node:

```
$parent = $node->getParent();

```

You can get the children of a node by using the following method:

```
$children = $node->getChildren();

```

> The `getChildren()` method returns only the direct descendants. If you want all descendants, use the `getDescendants()` method.

You can get the descendants or ancestors of a node by using the following methods:

```
$descendants = $node->getDescendants();
$ancestors = $node->getAncestors();

```

Sometimes you may just want to get the number of children or descendants. You can use the following methods to accomplish this:

```
$numChildren = $node->getNumberChildren();
$numDescendants = $node->getNumberDescendants();

```

The `getDescendants()` method accepts a parameter that you can use to specify the depth of the resulting branch. For example `getDescendants(1)` retrieves only the direct descendants (the descendants that are 1 level below, that's the same as `getChildren()`).

### Rendering a Simple Tree

[](#rendering-a-simple-tree)

```
$tree = $nsm->fetchTreeAsArray(1);

foreach ($tree as $node) {
    echo str_repeat('&nbsp;&nbsp;', $node->getLevel()) . $node . "";
}

```

Advanced Usage
--------------

[](#advanced-usage)

The previous sections have explained the basic usage of Doctrine's nested set implementation. This section will go one step further.

### Fetching a Tree with Relations

[](#fetching-a-tree-with-relations)

If you're a demanding software developer this question may already have come into your mind: "How do I fetch a tree/branch with related data?". Simple example: You want to display a tree of categories, but you also want to display some related data of each category, let's say some details of the hottest product in that category. Fetching the tree as seen in the previous sections and simply accessing the relations while iterating over the tree is possible but produces a lot of unnecessary database queries. Luckily, Manager and some flexibility in the nested set implementation have come to your rescue. The nested set implementation uses `QueryBuilder` objects for all it's database work. By giving you access to the base query builder of the nested set implementation you can unleash the full power of `QueryBuilder`while using your nested set.

```
$qb = $em->createQueryBuilder();
$qb->select('c.name, p.name, m.name')
    ->from('Category', 'c')
    ->leftJoin('c.HottestProduct', 'p')
    ->leftJoin('p.Manufacturer', 'm');

```

Now we need to set the above query as the base query for the tree:

```
$nsm->getConfiguration()->setBaseQueryBuilder($qb);
$tree = $nsm->fetchTree(1);

```

There it is, the tree with all the related data you need, all in one query.

> If you don't set your own base query then one will be automatically created for you internally.

When you are done it is a good idea to reset the base query back to normal:

```
$nsm->getConfiguration()->resetBaseQueryBuilder();

```

### Transactions

[](#transactions)

When modifying a tree using methods from `NodeWrapper`, each method is executed immediately. This differs from working with normal Doctrine2 entities where changes are queued via the EntityManager and not executed until `flush` is called.

If you are making multiple changes, it is recommended to wrap these changes in a transaction:

```
$em->getConnection()->beginTransaction();
try {

    $root = $nsm->createRoot(new Category('Root'));
    $root->addChild(new Category('Child 1'));
    $root->addChild(new Category('Child 2'));

    $em->getConnection()->commit();
} catch (Exception $e) {
    $em->close();
    $em->getConnection()->rollback();
    throw $e;
}

```

### Customizing left, right and root fields

[](#customizing-left-right-and-root-fields)

NestedSet requires you include left, right and root fields in your entity class. By default, NestedSet expects these fields to be named lft, rgt and root respectively. You can customize the names of these fields using via the manager configuration:

```
$config = new Config($em, 'Entity\Category');
$config->setLeftFieldName('nsLeft');
$config->setRightFieldName('nsRight');
$config->setRootFieldName('nsRoot');
$nsm = new Manager($config);

```

Conclusion
----------

[](#conclusion)

NestedSet makes managing hierarchical data in Doctrine2 quick and easy.

###  Health Score

32

—

LowBetter than 74% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity21

Limited adoption so far

Community17

Small or concentrated contributor base

Maturity59

Maturing project, gaining track record

 Bus Factor3

3 contributors hold 50%+ of commits

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

3486d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/394ba4724b1dea1531cf482420137461004ae839030048c178b5a18b336bd961?d=identicon)[nlzet](/maintainers/nlzet)

---

Top Contributors

[![CMatias](https://avatars.githubusercontent.com/u/5939385?v=4)](https://github.com/CMatias "CMatias (4 commits)")[![nlzet](https://avatars.githubusercontent.com/u/932439?v=4)](https://github.com/nlzet "nlzet (3 commits)")[![blt04](https://avatars.githubusercontent.com/u/276141?v=4)](https://github.com/blt04 "blt04 (3 commits)")[![jdodds](https://avatars.githubusercontent.com/u/126290?v=4)](https://github.com/jdodds "jdodds (2 commits)")[![justinpfister](https://avatars.githubusercontent.com/u/49603?v=4)](https://github.com/justinpfister "justinpfister (1 commits)")[![livsi](https://avatars.githubusercontent.com/u/307654?v=4)](https://github.com/livsi "livsi (1 commits)")[![mdelanno](https://avatars.githubusercontent.com/u/372405?v=4)](https://github.com/mdelanno "mdelanno (1 commits)")[![nulopes](https://avatars.githubusercontent.com/u/7762986?v=4)](https://github.com/nulopes "nulopes (1 commits)")[![calumbrodie](https://avatars.githubusercontent.com/u/459459?v=4)](https://github.com/calumbrodie "calumbrodie (1 commits)")[![fogs](https://avatars.githubusercontent.com/u/2734563?v=4)](https://github.com/fogs "fogs (1 commits)")[![jsmitka](https://avatars.githubusercontent.com/u/116015?v=4)](https://github.com/jsmitka "jsmitka (1 commits)")

### Embed Badge

![Health badge](/badges/nlzet-doctrine2-nestedset/health.svg)

```
[![Health](https://phpackages.com/badges/nlzet-doctrine2-nestedset/health.svg)](https://phpackages.com/packages/nlzet-doctrine2-nestedset)
```

###  Alternatives

[doctrine/orm

Object-Relational-Mapper for PHP

10.2k280.5M5.7k](/packages/doctrine-orm)[jdorn/sql-formatter

a PHP SQL highlighting library

3.9k114.4M90](/packages/jdorn-sql-formatter)[illuminate/database

The Illuminate Database package.

2.8k51.5M8.4k](/packages/illuminate-database)[mongodb/mongodb

MongoDB driver library

1.6k62.7M506](/packages/mongodb-mongodb)[maxmind-db/reader

MaxMind DB Reader API

69796.7M46](/packages/maxmind-db-reader)[spatie/db-dumper

Dump databases

1.2k25.1M58](/packages/spatie-db-dumper)

PHPackages © 2026

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