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

Laravel Tree Laravel Package

nevadskiy/laravel-tree

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require nevadskiy/laravel-tree
    

    Publish the migration (if needed) and run:

    php artisan vendor:publish --provider="Nevadskiy\Tree\TreeServiceProvider"
    
  2. Model Integration: Use the Tree trait in your Eloquent model:

    use Nevadskiy\Tree\Traits\Tree;
    
    class Category extends Model
    {
        use Tree;
    }
    
  3. First Use Case:

    • Create a root node:
      $root = Category::create(['name' => 'Root']);
      
    • Attach a child:
      $child = $root->children()->create(['name' => 'Child']);
      
    • Query the tree:
      $tree = Category::root()->withDescendants()->get();
      

Where to Look First

  • Documentation: Focus on the README and the materialized path pattern section.
  • Traits: Explore Tree, TreeScope, and TreeQueryBuilder in src/Traits.
  • Migrations: Check database/migrations/ for column requirements (e.g., path for materialized paths).

Implementation Patterns

Core Workflows

  1. Tree Construction:

    • Root Creation: Use Model::root() to create or fetch root nodes.
      $root = Category::root()->create(['name' => 'Electronics']);
      
    • Nested Creation: Chain children() for hierarchical creation:
      $subcategory = $root->children()->create(['name' 'Laptops']);
      
  2. Querying:

    • Scope Children: Use withDescendants() or withAncestors():
      $subtree = Category::find($id)->withDescendants()->get();
      
    • Path-Based Queries: Leverage wherePathContains() for filtering:
      $results = Category::wherePathContains('/1/')->get();
      
  3. Reordering:

    • Move nodes with move():
      $node->move($newParent, 'last'); // or 'first', 'before', 'after'
      
  4. Serialization:

    • Convert to nested arrays:
      $treeArray = $root->toTree();
      

Integration Tips

  • Foreign Keys: Ensure your parent_id column exists (though the package uses materialized paths, it respects parent_id for compatibility).
  • Caching: Cache frequent tree queries (e.g., Category::root()->withDescendants()).
  • Validation: Validate path updates in observers or model events to prevent corruption.
  • Testing: Use TreeTestCase (if provided) or mock tree behaviors in unit tests.

Gotchas and Tips

Pitfalls

  1. Path Corruption:

    • Issue: Manual path updates can break the tree structure.
    • Fix: Use move() or save() methods instead of direct DB updates.
    • Debug: Check path values with:
      $node->path; // Should match expected hierarchy (e.g., "/1/4/")
      
  2. Performance:

    • Issue: Deep trees may slow down withDescendants() queries.
    • Fix: Limit depth with maxDepth() or use lazy loading:
      $tree = Category::root()->withDescendants()->limitDepth(3)->get();
      
  3. Circular References:

    • Issue: Accidental loops (e.g., A is parent of B, B is parent of A).
    • Fix: Validate parent_id in model setParentIdAttribute:
      public function setParentIdAttribute($value)
      {
          if ($this->id === $value) {
              throw new \Exception("Cannot be its own parent.");
          }
          $this->attributes['parent_id'] = $value;
      }
      
  4. Migration Conflicts:

    • Issue: Existing path columns may conflict with the package’s migration.
    • Fix: Skip the migration if you’ve already set up the column:
      php artisan vendor:publish --provider="Nevadskiy\Tree\TreeServiceProvider" --tag="migrations" --force
      

Debugging Tips

  • Log Paths: Add a scope to log paths for debugging:
    Category::addGlobalScope('logPath', function (Builder $builder) {
        $builder->tap(function ($query) {
            \Log::debug('Querying paths:', [$query->getQuery()->wheres]);
        });
    });
    
  • Check Depth: Use depth() to verify hierarchy levels:
    $node->depth; // Should increment with each level (root = 0)
    

Extension Points

  1. Custom Path Separator:

    • Override the default / separator in the model:
      protected $pathSeparator = '.';
      
  2. Custom Scopes:

    • Extend TreeScope to add custom query logic:
      use Nevadskiy\Tree\Scopes\TreeScope;
      
      class CustomScope extends TreeScope
      {
          public function apply(Builder $builder, Model $model)
          {
              parent::apply($builder, $model);
              $builder->where('active', 1); // Example: Only active nodes
          }
      }
      
  3. Event Hooks:

    • Listen for tree.moved or tree.created events to trigger side effects:
      \Event::listen('tree.moved', function ($model) {
          // Update related data (e.g., cache, notifications)
      });
      
  4. Alternative Storage:

    • For non-materialized paths, disable the trait’s path logic and use parent_id only:
      use Nevadskiy\Tree\Traits\Tree as BaseTree;
      
      trait CustomTree extends BaseTree
      {
          protected $useMaterializedPath = false;
      }
      
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.
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle
atriumphp/atrium
sandermuller/package-boost-laravel
sandermuller/boost-skills
redaxo/core
yusufgenc/filament-api-forge
l3aro/rating-star-for-filament
leek/filament-subtenant-scope