PHPackages                             goutte/tree-bundle - 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. goutte/tree-bundle

ActiveSymfony-bundle[Utility &amp; Helpers](/categories/utility)

goutte/tree-bundle
==================

Goutte's Rooted Tree Library

v1.7.5(13y ago)31.0k1MITPHPPHP &gt;=5.3.3

Since Dec 11Pushed 13y ago1 watchersCompare

[ Source](https://github.com/Goutte/TreeBundle)[ Packagist](https://packagist.org/packages/goutte/tree-bundle)[ Docs](https://github.com/Goutte/TreeBundle)[ RSS](/packages/goutte-tree-bundle/feed)WikiDiscussions master Synced 1mo ago

READMEChangelogDependencies (4)Versions (12)Used By (0)

TreeBundle
==========

[](#treebundle)

The Tree structure is currently designed with only one model, the [Node](https://github.com/Goutte/TreeBundle/blob/master/Model/AbstractNode.php).

Provides a service for serializing and unserializing nodes, to and from strings such as `A(B,C(D))`.

Drivers provided :

- Parenthesis : `A(B,C(D))`
- Simple (dumb!) [timbre](https://github.com/mohayonao/timbre) : `T("*",T(6),T("sin",T(55.2)))`
- Ascii

    ```
    A
    +--B
    |  +--C
    |  +--D
    |     +--E
    +--Fork

    ```

See the [Tests](https://github.com/Goutte/TreeBundle/tree/master/Tests/Driver) for more examples of what the Drivers support.

How to Use
==========

[](#how-to-use)

This is *not* for storing nested sets in the database, if that is what you are looking for you should be looking at [Doctrine Extensions](https://github.com/l3pp4rd/DoctrineExtensions). The original purpose of this bundle is to provide a toolset for reading/writing functional code, or configuring the hierarchy of a menu in plain text for example.

Feel free to extend it to suit your needs, though.

Install
-------

[](#install)

Add this bundle to your project using composer

```
composer require goutte/tree-bundle

```

Service
-------

[](#service)

Use the service from the container :

```
    // get the serializer service
    $serializer = $container->get('goutte_tree.serializer');

    // this will create the nodes and return the root node
    $rootNode = $serializer->toNode('root(childA,childB(grandchildC))');

    // this will return the string for the subtree below the passed node
    $string = $serializer->toString($rootNode); // returns 'root(childA,childB(grandchildC))'

    // ... or use another driver
    $string = $serializer->useDriver('ascii')->toString($rootNode);
    // will return:
    // root
    // +--childA
    // +--childB
    //    +--grandchildC
```

See `Goutte\TreeBundle\Is\Node` for a list of the methods provided by the abstract class `Goutte\TreeBundle\Model\AbstractNode`.

Using your own Node
-------------------

[](#using-your-own-node)

### Extending

[](#extending)

```
    use Goutte\TreeBundle\Model\Node as AbstractNode;
    class MyNode extends AbstractNode {
        // ...
    }
```

### Implementing

[](#implementing)

```
    use Goutte\TreeBundle\Is\Node as NodeInterface;
    class MyNode implements NodeInterface {
        // ...
    }
```

Then, configure the service to use your own Node class.

Writing a Driver
----------------

[](#writing-a-driver)

Implement `Goutte\TreeBundle\Is\Driver` as follows :

```
    use Goutte\TreeBundle\Is\Driver;
    use Goutte\TreeBundle\Factory\NodeFactoryInterface;

    class MyDriver implements Driver
    {
        protected $factory

        public function __construct(NodeFactoryInterface $factory)
        {
            $this->factory = $factory;
        }

        // ... implement Driver
    }
```

The `__construct` part is optional, but you'll probably want a NodeFactory to create Nodes. We provide a default Node Factory as service, see the `` in the service definition below.

Add your driver to your `services.xml`, and tag it `goutte_tree.driver` :

```

```

**(warn)** The service id will define the driver alias, so it needs to start with `goutte_tree.driver.`. It is not the documented way of doing such a thing (which would be having an alias attribute in the tag), but it is a bit DRYer. This may be subject to change later, I'm still making up my mind.

Configure the service to use your custom driver with `->useDriver()` :

```
    // Get the service, tell it to use your driver
    $serializer = $container->get('goutte_tree.serializer')->useDriver('mydriver');
```

You may skip usage of `->useDriver()` by telling the service to use your driver as default in the `services.xml` :

```

```

Testing
=======

[](#testing)

This bundle is ruthlessly tested, except for some service configuration exceptions (if you know how to test these, I'm all ears!)

Run composer with the `--dev` option so that the autoloader is created and the needed sf2 DIC classes are autoloaded. *Oddly enough, when I tried to install with `--test` and `require-test` in the `composer.json`, I was sent packing. (pun intended)*

```
composer install --dev

```

Then, simply run

```
phpunit

```

Pitfalls
========

[](#pitfalls)

### Parenthesis Driver

[](#parenthesis-driver)

Nodes with empty label can convert to string, but not back to node.

Eg: `A(B,C)` tree, if nodes' labels are emptied, will convert back to `(,)`

Envisioned solutions :

- Throw on toString conversion if label is empty -&gt; loss of feature
- Tweak the toNode regex to allow empty labels -&gt; disturbing as `A()` will create two nodes for example

### Timbre Driver

[](#timbre-driver)

The nodes labels are not escaped by the driver, so no `(`, `)` or `,`. As these characters are not used by Timbre's nodes, this should not be a problem.

Numerical labels must be encapsulated in `T()`, like so : `T(66.2)`.

### Ascii Driver

[](#ascii-driver)

Linebreaks will be (un)escaped so that labels stay on one line in the string representation.

Because the reader expects *exactly 2* `-` as indentation, the Node label will hold the extra `-` if you add more.

Eg:

```
A
+---B

```

=&gt; The child node's label will be `-B`, not `B`.

ChangeLog
=========

[](#changelog)

v1.0
----

[](#v10)

- TreeIntegrityException
- DriverException
- Node base methods
- Path finding
- Tree integrity tests
- DIC for Drivers
- Documentation

v1.5
----

[](#v15)

- Parenthesis driver
- Timbre driver
- AsciiDriver for multiline ascii rooted trees
- Node replacement with -&gt;replaceBy()

v1.6
----

[](#v16)

- Node cloning

v1.7.1
------

[](#v171)

- -&gt;getDescendants() (breadth-first TWA by default)
- -&gt;getRandomDescendant($includeSelf=false)

v1.7.4
------

[](#v174)

- Decoupling the Random util for easier/deeper deteministic testing
- -&gt;removeChildren()

v1.7.5
------

[](#v175)

- Usage of Factories in Drivers

RoadMap
=======

[](#roadmap)

*These have no schedule, don't wait for them.*

v1.8
----

[](#v18)

- -&gt;getAncestors()
- -&gt;getRandomAncestor($includeSelf=false)

v2.0
----

[](#v20)

- Empty labels in parenthesis driver
- Graph theory, see the [BLACKBOARD](https://github.com/Goutte/TreeBundle/blob/master/BLACKBOARD.markdown)
    - -&gt; probably in another bundle
- Custom tree walking for tree flattening
    - breadth-first
    - depth-first

###  Health Score

31

—

LowBetter than 68% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity19

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity65

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 ~3 days

Total

9

Last Release

4880d ago

Major Versions

v0.1 → v1.02012-12-14

### Community

Maintainers

![](https://www.gravatar.com/avatar/d6ede3d9d0f69df21946214e2abc305bebfa0978bffc5a879933f3851d47d21f?d=identicon)[Goutte](/maintainers/Goutte)

---

Top Contributors

[![Goutte](https://avatars.githubusercontent.com/u/592545?v=4)](https://github.com/Goutte "Goutte (71 commits)")

---

Tags

treenode

### Embed Badge

![Health badge](/badges/goutte-tree-bundle/health.svg)

```
[![Health](https://phpackages.com/badges/goutte-tree-bundle/health.svg)](https://phpackages.com/packages/goutte-tree-bundle)
```

###  Alternatives

[knplabs/knp-menu

An object oriented menu library

1.4k55.8M287](/packages/knplabs-knp-menu)[cuyz/valinor

Dependency free PHP library that helps to map any input into a strongly-typed structure.

1.5k9.2M108](/packages/cuyz-valinor)[bluem/tree

Library for handling tree structures based on parent IDs

252916.1k7](/packages/bluem-tree)[codewithdennis/filament-select-tree

The multi-level select field enables you to make single selections from a predefined list of options that are organized into multiple levels or depths.

320392.1k17](/packages/codewithdennis-filament-select-tree)[loophp/phptree

An implementation of tree data structure

981.8M2](/packages/loophp-phptree)[kartik-v/yii2-tree-manager

An enhanced tree management module with tree node selection and manipulation using nested sets.

156529.0k15](/packages/kartik-v-yii2-tree-manager)

PHPackages © 2026

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