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 View Laravel Package

openplain/filament-tree-view

Drag-and-drop tree view for Filament resources to manage hierarchical data. Built on Laravel Adjacency List and Atlassian Pragmatic Drag & Drop. Supports depth limits, auto or batch save, custom fields, actions, dark mode, accessibility, and safe moves.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation:

    composer require openplain/filament-tree-view
    php artisan filament:assets
    
  2. Database Setup: Add parent_id (nullable foreign key) and order columns to your migration:

    Schema::create('categories', function (Blueprint $table) {
        $table->id();
        $table->string('name');
        $table->foreignId('parent_id')->nullable()->constrained();
        $table->integer('order')->default(0);
        $table->timestamps();
    });
    
  3. Model Setup: Add the trait to your model:

    use Openplain\FilamentTreeView\Concerns\HasTreeStructure;
    
    class Category extends Model
    {
        use HasTreeStructure;
        // ...
    }
    
  4. Resource Integration: Add tree() method to your Filament resource:

    public static function tree(Tree $tree): Tree
    {
        return $tree->fields([
            TextField::make('name'),
        ]);
    }
    
  5. Page Creation: Create a page extending TreePage:

    class TreeCategories extends TreePage
    {
        protected static string $resource = CategoryResource::class;
    }
    

First Use Case

Replace a standard table() with tree() for hierarchical data management. Example:

public static function tree(Tree $tree): Tree
{
    return $tree
        ->fields([
            TextField::make('title'),
            IconField::make('is_published'),
        ])
        ->recordActions([
            EditAction::make(),
            DeleteAction::make(),
        ]);
}

Implementation Patterns

Core Workflow

  1. Configuration:

    • Define fields using TextField/IconField (required)
    • Customize behavior with methods like maxDepth(), collapsed(), autoSave()
    • Add actions via recordActions()
  2. Page Integration:

    • Use TreePage for standalone trees
    • Use TreeRelationPage for related records (e.g., /{parent}/children)
  3. Data Management:

    • Drag-and-drop reordering updates parent_id and order columns
    • Auto-save (autoSave()) or manual save modes available
    • Depth limits prevent overly complex hierarchies

Common Patterns

Hierarchical Relationships

// In parent resource
public static function getPages(): array
{
    return [
        'children' => Pages\ManageChildren::route('/{record}/children'),
    ];
}

// In child resource
class ManageChildren extends TreeRelationPage
{
    protected static string $relationship = 'children';
}

Custom Field Styling

TextField::make('name')
    ->weight(FontWeight::Bold)
    ->color('primary')
    ->dimWhenInactive('is_active')

Action Patterns

// Bulk actions
->bulkActions([
    Action::make('publish')
        ->action(fn (Collection $records) => $records->update(['is_published' => true])),
]),

// Conditional actions
->recordActions(fn (Category $record): array => [
    DeleteAction::make()->visible(fn () => $record->children()->count() === 0),
]),

Query Customization

// Filter roots only
->query(fn (Builder $query) => $query->whereNull('parent_id')),

// Custom ordering
->query(fn (Builder $query) => $query->orderBy('name')),

Gotchas and Tips

Pitfalls

  1. Missing Fields:

    • Error: Fields are required for TreeView
    • Fix: Always include at least one field in ->fields([])
  2. Circular References:

    • Attempting to move a node under itself causes silent failure
    • Fix: Use maxDepth() to prevent deep nesting or validate moves in beforeSave()
  3. Performance with Large Trees:

    • Deep trees (>5 levels) may lag with many nodes
    • Fix: Use collapsed() by default and implement lazy-loading
  4. UUID Primary Keys:

    • Default trait assumes integer id column
    • Fix: Override getLocalKeyName() and ensure parent_id uses UUID type

Debugging Tips

  1. Check Tree Structure:

    // Verify parent/child relationships
    $category->children()->count();
    $category->parent?->name;
    
  2. Inspect Queries: Enable Laravel query logging to verify tree queries:

    DB::enableQueryLog();
    $tree->getRecords();
    dd(DB::getQueryLog());
    
  3. Clear Caches: After model/trait changes:

    php artisan optimize:clear
    php artisan view:clear
    

Extension Points

  1. Custom Save Logic: Override beforeSave() in your model:

    public function beforeSave(Tree $tree, array $data): void
    {
        // Validate moves
        if ($data['parent_id'] === $this->id) {
            throw new \Exception('Cannot move node under itself');
        }
    }
    
  2. Custom Field Types: Extend TreeField for custom fields:

    class StatusField extends TreeField
    {
        protected string $view = 'filament-tree-view::fields.status';
    }
    
  3. Custom Styling: Publish assets and override SCSS:

    php artisan vendor:publish --tag=filament-tree-view-assets
    

    Then modify resources/css/filament-tree-view.css.

Configuration Quirks

  1. Auto-Save Behavior:

    • autoSave() triggers on every drag operation
    • For large trees, consider batching updates or using maxDepth()
  2. Depth Calculation:

    • Virtual depth attribute is calculated on-the-fly
    • For performance, avoid deep queries on large trees
  3. Relation Pages:

    • Ensure $relationship in TreeRelationPage matches your model's relation name
    • Example: protected static string $relationship = 'subcategories';

Pro Tips

  1. Combine with Filament Actions:

    ->recordActions([
        Action::make('duplicate')
            ->action(fn (Category $record) => $record->replicate()),
    ]),
    
  2. Add Search:

    ->query(fn (Builder $query) => $query->where('name', 'like', '%'.$search.'%')),
    
  3. Dark Mode Support: The package automatically adapts to Filament's dark mode—no extra config needed.

  4. Accessibility:

    • Keyboard navigation works out-of-the-box
    • Screen readers announce tree structure and actions
  5. Testing: Use the TreeTestCase helper for testing:

    use Openplain\FilamentTreeView\Testing\TreeTestCase;
    
    class CategoryResourceTest extends TreeTestCase
    {
        public function test_tree_reordering()
        {
            $this->actingAs($user)
                ->drag('node-1', 'node-2')
                ->assertTreeOrder(['node-2', 'node-1']);
        }
    }
    
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.
nexmo/api-specification
capell-app/block-library
axium/identity
cetria/laravel-dummy-models
cetria/reflection-helper
agropredict/sso-auth-bundle
evolvestudio/spam-protection
datacore/hub-sdk
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony
develia/geo-bundle
dreamzy/livewire-charts
touchestate-sdk/php-sdk
ecotone/kafka
22h/doctrine-garbage-collection-bundle
agtp/agtp-php
agtp/mod-php
splash/sonata-admin
splash/metadata