Weave Code
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 Laravel Package

nicmart/tree

A lightweight, flexible tree data structure for PHP. Provides NodeInterface/Node implementations with parent/child management, leaf/child checks, and easy child add/remove/set operations. Includes fluent builder classes to assemble trees quickly.

View on GitHub
Deep Wiki
Context7

Getting Started

Install via Composer: composer require nicmart/tree. Start by importing the core interfaces and classes: Tree\TreeNode (now generic) and Tree\TreeBuilder. The most common first use case is modeling hierarchical data like product categories or navigation menus.

For strongly-typed node values, leverage the generic TreeNode:

use Tree\TreeNode;

// Define a typed node for ProductCategories
$root = new TreeNode<string>('Electronics');
$phones = $root->addChild(new TreeNode<string>('Phones'));
$phones->addChild(new TreeNode<string>('Smartphones'));

For flat data integration (e.g., database rows), use TreeBuilder:

use Tree\TreeBuilder;

$nodes = [
    ['id' => 1, 'parent_id' => null, 'name' => 'Root'],
    ['id' => 2, 'parent_id' => 1, 'name' => 'Child'],
];
$tree = (new TreeBuilder())->build($nodes, 'id', 'parent_id', TreeNode::class);

Implementation Patterns

  • Generic Node Types: Use TreeNode<T> to enforce type safety for node values (e.g., TreeNode<Category>, TreeNode<string>). This prevents runtime errors when mixing incompatible data types.

    class Category { public string $name; public ?int $parentId; }
    $root = new TreeNode<Category>(new Category());
    
  • Custom Node Classes: Extend TreeNode<T> to include domain logic. Override clone() for deep-copy safety when moving nodes:

    class CategoryNode extends TreeNode<Category> {
        public function getBreadcrumbs(): array { /* ... */ }
    }
    
  • Traversing with Iterators: Use TreeIterator for depth-first/breadth-first traversal, with options to skip subtrees or limit depth. Ideal for rendering menus or calculating metrics:

    $iterator = new TreeIterator($root);
    $iterator->setMaxDepth(2); // Limit traversal depth
    
  • Serialization/Deserialization: Convert trees to/from arrays via toArray()/fromArray() (via TreeBuilder). Specify the generic type for accurate reconstruction:

    $array = $root->toArray();
    $rebuilt = (new TreeBuilder())->fromArray($array, TreeNode::class);
    
  • Reparenting & Movement: Use $node->moveTo($parent) to reassign children safely. The library enforces cycle prevention automatically.

  • Laravel/Eloquent Integration: Store tree metadata in the database (e.g., nestedset or closure_table), but use nicmart/tree for in-memory manipulation. Hydrate nodes from query results with explicit typing:

    $nodes = Category::all()->map(fn($cat) => new CategoryNode($cat));
    $tree = (new TreeBuilder())->build($nodes, 'id', 'parent_id');
    

Gotchas and Tips

  • Generic Type Safety: Ensure all TreeNode<T> instances use the same generic type when building trees. Mixing types (e.g., TreeNode<string> and TreeNode<Category>) in the same tree will cause runtime errors.

    // ❌ Avoid mixing types
    $mixedTree = new TreeNode<string>('Root');
    $mixedTree->addChild(new TreeNode<Category>(new Category())); // Error
    
  • Identity vs. Equality: Nodes are compared by object identity (not data). Two TreeNode<T> instances with identical values are not considered equal unless they’re the same instance. Avoid unintentional duplication when cloning or moving.

  • Circular References: Cycle detection is automatic, but manually editing parent/child links (e.g., $child->setParent($child)) will throw. Use official APIs like addChild()/moveTo().

  • Performance with Large Trees: Deep recursion (e.g., getAncestors(), depthFirstWalk()) may hit stack limits. Use iterative approaches or limit traversal depth with TreeIterator::setMaxDepth().

  • Extensibility Hooks: Override TreeNode<T>::canAttach() to enforce custom constraints (e.g., max children, type restrictions). Implement JsonSerializable for tailored JSON output:

    class CategoryNode extends TreeNode<Category> implements JsonSerializable {
        public function jsonSerialize(): array { /* ... */ }
    }
    
  • Debugging: Use $node->dump() for quick visual inspection. For complex debugging, attach Tree\TreeVisitor implementations or leverage Xdebug with generic type hints.

  • Backward Compatibility: The generic TreeNode is now required for all new instances. Existing non-generic TreeNode usage will continue to work but is deprecated. Update to TreeNode<T> for future compatibility.

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.
davejamesmiller/laravel-breadcrumbs
artisanry/parsedown
christhompsontldr/phpsdk
enqueue/dsn
bunny/bunny
enqueue/test
enqueue/null
enqueue/amqp-tools
bower-asset/punycode
bower-asset/inputmask
bower-asset/jquery
bower-asset/yii2-pjax
laravel/nova
spatie/laravel-mailcoach
spatie/laravel-superseeder
laravel/liferaft
nst/json-test-suite
danielmiessler/sec-lists
jackalope/jackalope-transport
twbs/bootstrap4