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

Baum Laravel Package

gazsp/baum

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require gazsp/baum
    

    Add the trait to your Eloquent model:

    use Gazsp\Baum\NodeTrait;
    
  2. Database Migration: Add lft and rgt columns to your table:

    Schema::table('categories', function (Blueprint $table) {
        $table->integer('lft')->unsigned();
        $table->integer('rgt')->unsigned();
    });
    
  3. First Use Case:

    // Create a root node
    $root = Category::create(['name' => 'Root']);
    $root->save(); // Automatically sets lft/rgt
    
    // Create a child
    $child = $root->appendChild(['name' => 'Child']);
    $child->save();
    

Key First Commands

  • Tree traversal:

    $root->children; // All direct children
    $root->descendants; // All descendants (recursive)
    $root->ancestors; // All ancestors
    
  • Positioning:

    $node->moveBefore($sibling); // Reorders nodes
    

Implementation Patterns

Common Workflows

  1. Building Hierarchies:

    // Create a multi-level tree
    $root = Category::create(['name' => 'Root']);
    $level1 = $root->appendChild(['name' => 'Level 1']);
    $level2 = $level1->appendChild(['name' => 'Level 2']);
    $level2->save(); // Saves entire subtree
    
  2. Querying:

    // Find all nodes under a specific path
    $subtree = Category::where('lft', '>=', $parent->lft)
                       ->where('rgt', '<=', $parent->rgt)
                       ->get();
    
    // Find siblings
    $siblings = $parent->parent->children;
    
  3. Bulk Operations:

    // Reorder children
    $parent->children->sortBy('name')->each->save();
    

Integration Tips

  • Scopes:

    // Add a scope to filter by depth
    public function scopeAtDepth($query, $depth) {
        return $query->where('lft', '>=', $depth * 2)
                    ->where('rgt', '<=', ($depth + 1) * 2 - 1);
    }
    
  • Events:

    // Listen for tree changes
    Category::saved(function ($model) {
        if ($model->wasChanged('lft') || $model->wasChanged('rgt')) {
            // Handle tree reordering
        }
    });
    
  • API Responses:

    // Serialize tree structure
    $tree = $root->toTreeArray(); // Returns nested array
    

Gotchas and Tips

Pitfalls

  1. Concurrency Issues:

    • Baum uses lft/rgt for ordering. Concurrent save() calls on siblings may corrupt the tree.
    • Fix: Use transactions or lock rows during reordering:
      DB::transaction(function () use ($node) {
          $node->moveBefore($sibling);
      });
      
  2. Performance with Large Trees:

    • Deep trees (>1000 nodes) may cause timeouts during save() due to recursive calculations.
    • Fix: Limit depth or use Baum::rebuild() sparingly.
  3. Circular References:

    • Attempting to make a node its own ancestor (e.g., node->appendChild($node)) will fail silently.
    • Fix: Validate parent-child relationships manually:
      if ($node->isDescendantOf($child)) {
          throw new \Exception("Cannot create circular reference");
      }
      

Debugging

  • Verify Tree Integrity:

    // Check if lft/rgt values are valid
    if ($node->lft > $node->rgt) {
        throw new \Exception("Invalid tree structure");
    }
    
  • Log Tree Changes:

    // Override save to log changes
    public function save(array $options = []) {
        $this->fireModelEvent('saving', false);
        $originalLft = $this->getOriginal('lft');
        $originalRgt = $this->getOriginal('rgt');
        $result = parent::save($options);
        if ($originalLft !== $this->lft || $originalRgt !== $this->rgt) {
            \Log::info("Tree reordered: {$this->name} (lft: {$this->lft}, rgt: {$this->rgt})");
        }
        return $result;
    }
    

Configuration Quirks

  1. Custom Column Names:

    • Override default lft/rgt columns:
      class Category extends Model {
          use NodeTrait;
      
          protected $baum = [
              'columns' => ['left' => 'lft_col', 'right' => 'rgt_col']
          ];
      }
      
  2. Root Node Handling:

    • Baum assumes the first node is the root. To enforce this:
      // In a model observer
      Category::creating(function ($model) {
          if (!Category::whereNull('parent_id')->exists()) {
              $model->parent_id = null;
          }
      });
      

Extension Points

  1. Custom Tree Calculations:

    • Override getDepth() or getLevel() for custom logic:
      public function getDepth() {
          return $this->parent ? $this->parent->depth + 1 : 0;
      }
      
  2. Hooks for Tree Operations:

    • Extend Gazsp\Baum\Node to add pre/post hooks:
      class CustomNode extends \Gazsp\Baum\Node {
          public function beforeMove() {
              // Custom logic before moving
          }
      }
      
  3. Alternative Storage:

    • For non-MySQL databases, implement a custom TreeBuilder:
      class CustomTreeBuilder extends \Gazsp\Baum\TreeBuilder {
          public function build($model) {
              // Custom tree-building logic
          }
      }
      
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.
comsave/common
alecsammon/php-raml-parser
chrome-php/wrench
lendable/composer-license-checker
typhoon/reflection
mesilov/moneyphp-percentage
mike42/gfx-php
bookdown/themes
aura/view
aura/html
aura/cli
povils/phpmnd
nayjest/manipulator
omnipay/tests
psr-mock/http-message-implementation
psr-mock/http-factory-implementation
psr-mock/http-client-implementation
voku/email-check
voku/urlify
rtheunissen/guzzle-log-middleware