Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Tree Bundle Laravel Package

goutte/tree-bundle

A Symfony bundle for building and managing hierarchical tree structures, with Doctrine integration, nested-set support, and utilities for rendering, traversing, and manipulating trees in PHP applications.

View on GitHub
Deep Wiki
Context7

Getting Started

1. Verify what you're actually installing. The description says "library", the name says "bundle" — check both before wiring anything up:

composer require goutte/tree-bundle
ls vendor/goutte/tree-bundle
cat vendor/goutte/tree-bundle/composer.json

Look for a *Bundle.php class and a DependencyInjection/ directory. If they exist, it's a Symfony bundle; register it the old way (AppKernel::registerBundles() or config/bundles.php depending on your Symfony version). If not, treat it as a plain library and skip registration entirely — most of the value is in the node/tree classes, not in container wiring.

2. Read the tests before the README. With 3 stars and no released docs, tests/ is the real specification. Open the test cases for the tree/node classes first — they show the canonical construction, attachment, and traversal API in a way the README almost certainly doesn't. Skim src/ namespaces next; the public surface is usually small (a node abstraction, a tree/root wrapper, and a traversal or visitor helper).

3. First use case: an in-memory tree, no persistence. Don't start with Doctrine. Instantiate a root node, attach a couple of children, and walk it:

$root = new Node('root');
$child = $root->addChild(new Node('child'));   // confirm exact method name in tests
$child->addChild(new Node('grandchild'));

foreach ($root->getDescendants() as $node) {   // or a visitor, if that's the API
    echo $node->getValue(), PHP_EOL;
}

Confirm three things immediately: how a node is attached, how children are read back, and whether the tree enforces single-parent invariants for you or expects you to maintain them. Those three answers determine how much of your own code you'll be writing around it.

Implementation Patterns

Build trees from flat adjacency lists. This is the day-to-day job — menus, category trees, org charts, comment threads. Load rows as [id, parent_id, label], index them by id, then attach each node to its parent in one pass. This is O(n) and avoids per-node queries:

$nodes = [];
foreach ($rows as $row) {
    $nodes[$row['id']] = new Node($row['label']);
}
foreach ($rows as $row) {
    if ($row['parent_id'] !== null) {
        $nodes[$row['parent_id']]->addChild($nodes[$row['id']]);
    }
}
$root = $nodes[$rootId];

Handle orphans and cycles explicitly: collect rows whose parent is missing, and assert that a node never ends up added below one of its own descendants.

Serialize to nested arrays for API responses. Recursive descent over children produces the JSON shape frontends expect, and keeps the tree abstraction out of your controllers:

function toArray(Node $node): array
{
    return [
        'label'    => $node->getValue(),
        'children' => array_map('toArray', $node->getChildren()),
    ];
}

If your node class exposes depth or path metadata, include it here rather than recomputing it in the view layer.

Wrap traversal behind a service. The bundle's traversal primitives are low-level. Put a small application service (TreeBuilder, TreeReader) in front of them so your domain code depends on your interface, not on a 3-star package. That one indirection is what makes the "vendor it and patch it" strategy in the Gotchas section survivable.

Persisting with Doctrine: store adjacency, not the tree. Keep parent_id on your entity and rebuild the tree per request. Do not attempt to map the node objects directly unless the bundle ships an explicit Doctrine mapping — and it probably doesn't. For deep or frequently-read trees, a closure table or a recursive CTE query in the repository is a better fit than hydrating the whole structure.

Test against fixtures, not mocks. A tree is cheap to construct inline; assert on your serialized output rather than on internal node state. Fixtures like "three levels, two siblings, one orphan" catch the failures that actually happen.

Gotchas and Tips

The license field is NOASSERTION. That means Composer couldn't identify a recognized license, not that there is none. Open LICENSE in the repo and get sign-off before shipping this in anything commercial. This is a hard blocker for most teams and is worth resolving before you write a line of integration code.

Treat this as unmaintained. 3 stars, 0 dependents, a package score near zero, and a Symfony2-era shape. Check the composer.json symfony/* constraints — if it pins ~2.x, it will not install cleanly on Symfony 5/6 without --ignore-platform-reqs or a fork. Plan on forking it into your own namespace and patching the constraints; that's cheaper than fighting the resolver every composer update.

Invariants are likely yours to enforce. Rooted-tree libraries commonly let you add the same node to two parents, or create a cycle, and only fail later during traversal (infinite recursion, stack overflow). Add guards in your wrapper: reject a node that already has a parent, and walk up from the candidate parent to confirm the node isn't its own ancestor.

N+1 on lazy-loaded children. If you ever back a node with a Doctrine relation, getChildren() in a loop triggers one query per node. Never traverse a lazy tree in a loop; hydrate the whole structure once, or use ->fetchJoin() at the top level.

Debugging deep trees. var_dump/print_r on a recursive structure explodes. Debug with a depth-limited walker and Xdebug's max_nesting_level raised, and log depth alongside the node value so you can spot a runaway branch immediately.

Extension points. With no docs, subclassing a node to add domain fields is the most likely integration route — keep your subclass thin and delegate traversal to the parent API. If the bundle offers a visitor interface, prefer it over recursion in your own code; that's usually the most stable part of an old package's surface.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource