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

Filament Tree Laravel Package

solution-forest/filament-tree

Filament Tree adds a drag-and-drop hierarchical tree UI to Filament Admin for managing nested data (menus, categories, org charts) with unlimited depth. Works with Resources, Pages, and Widgets, plus customizable actions, icons, and translations.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Install the package:

    composer require solution-forest/filament-tree
    php artisan filament:assets
    
  2. Create a migration with tree columns:

    Schema::create('categories', function (Blueprint $table) {
        $table->id();
        $table->treeColumns(); // Adds parent_id, order, title
        $table->timestamps();
    });
    
  3. Add the ModelTree trait to your model:

    use SolutionForest\FilamentTree\Concern\ModelTree;
    
    class Category extends Model
    {
        use ModelTree;
    }
    
  4. Generate a tree widget/page:

    php artisan make:filament-tree-widget CategoryWidget --model=Category
    

First Use Case

Embed a tree widget in a Filament resource page:

// In your resource's ListRecords page
protected function getHeaderWidgets(): array
{
    return [CategoryWidget::class];
}

Implementation Patterns

Common Workflows

1. Basic Tree Widget Integration

// app/Filament/Widgets/CategoryWidget.php
class CategoryWidget extends \SolutionForest\FilamentTree\Widgets\Tree
{
    protected static string $model = Category::class;
    protected static int $maxDepth = 3;

    protected function getFormSchema(): array
    {
        return [
            TextInput::make('title')->required(),
        ];
    }
}

2. Tree Page with Custom Actions

// app/Filament/Pages/CategoryTree.php
class CategoryTree extends \SolutionForest\FilamentTree\Pages\TreePage
{
    protected static string $resource = CategoryResource::class;

    protected function getTreeToolbarActions(): array
    {
        return [
            CreateAction::make()->label('Add Category'),
            ExportAction::make()->label('Export Tree'),
        ];
    }

    public function getTreeRecordTitle(?Category $record): string
    {
        return "[{$record->id}] {$record->title}";
    }
}

3. Resource-Specific Tree Page

php artisan make:filament-tree-page CategoryTree --resource=Category

Register in resource:

public static function getPages(): array
{
    return [
        'tree' => Pages\CategoryTree::route('/tree'),
    ];
}

Integration Tips

  • For nested resources: Use $maxDepth to control nesting levels
  • For performance: Pre-collapse nodes with getNodeCollapsedState()
  • For translations: Use Translatable trait with Spatie Translatable
  • For custom queries: Override getTreeQuery() in widgets/pages

Gotchas and Tips

Common Pitfalls

  1. Parent ID Default Value

    • ❌ Wrong: parent_id->default(0)
    • ✅ Correct: parent_id->default(-1) (required for root nodes)
  2. Missing Asset Registration

    • Run php artisan filament:assets after installation
  3. Column Name Mismatches

    • Ensure your model uses the correct column names or override them:
      public function determineParentColumnName(): string
      {
          return 'parent_category_id';
      }
      
  4. Depth Limitations

    • Set $maxDepth to prevent infinite nesting:
      protected static int $maxDepth = 5;
      

Debugging Tips

  • Check Tree Structure: Use dd($record->getTree()) to inspect relationships
  • Verify Queries: Enable query logging in .env:
    DB_LOG_QUERIES=true
    
  • Clear Cache: After configuration changes:
    php artisan optimize:clear
    

Extension Points

  1. Custom Tree Icons

    public function getTreeRecordIcon(?Category $record): ?string
    {
        return match ($record->type) {
            'folder' => 'heroicon-o-folder',
            'file' => 'heroicon-o-document',
            default => null,
        };
    }
    
  2. Conditional Node Display

    public function getTreeRecordTitle(?Category $record): string
    {
        if (!$record->is_active) {
            return "🚫 {$record->title}";
        }
        return $record->title;
    }
    
  3. Toolbar Actions

    protected function getTreeToolbarActions(): array
    {
        return [
            Action::make('custom-action')
                ->label('Custom Action')
                ->action(fn () => $this->dispatchBrowserEvent('custom-event')),
        ];
    }
    

Configuration Quirks

  • Default Parent ID: Must be -1 (not null or 0)
  • Order Column: Should be indexed for performance
  • Title Column: Must match determineTitleColumnName() if customized

Performance Optimization

  1. Eager Load Relationships

    protected function getTreeQuery(): Builder
    {
        return Category::with('children')->query();
    }
    
  2. Limit Depth

    protected static int $maxDepth = 3;
    
  3. Collapse Nodes by Default

    public function getNodeCollapsedState(?Category $record): bool
    {
        return $record->getDepth() > 1;
    }
    

Translation Support

  1. Add Traits

    use SolutionForest\FilamentTree\Concern\TreeRecords\Translatable;
    use Spatie\Translatable\HasTranslations;
    
    class Category extends Model
    {
        use HasTranslations, ModelTree;
    
        protected $translatable = ['title'];
    }
    
  2. Configure Locales

    public function getTranslatableLocales(): array
    {
        return ['en', 'fr', 'es'];
    }
    

Custom Column Names

Override defaults in your model:

public function determineOrderColumnName(): string
{
    return 'sort_order';
}

public function determineParentColumnName(): string
{
    return 'parent_category_id';
}

Toolbar Action Limitations

  • Only available in v3.1.0+
  • Requires explicit registration in getTreeToolbarActions()

Drag-and-Drop Behavior

  • Hold Shift to move nodes between branches
  • Hold Ctrl/Cmd to copy nodes (if implemented)
  • Double-click to edit (configurable via actions)
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.
codeflextech/permission-manager
karnoweb/livewire-datepicker
sayedenam/sayed-dashboard
milito/query-filter
apiboxsym/user-bundle
apiboxsym/health-check-bundle
jayeshmepani/jpl-moshier-ephemeris-php
elnasnato/laraliveui
labrodev/rest-sdk
sampaui/sampaui
babelqueue/php-sdk
facebook/capi-param-builder-php
babelqueue/symfony
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver