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 Select Tree Laravel Package

codewithdennis/filament-select-tree

Dynamic select tree field for Laravel Filament that renders hierarchical dropdowns from relationships or custom queries. Supports BelongsTo and BelongsToMany, parent/child attributes, and customizable parent/child query scopes with optional strict root behavior.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require codewithdennis/filament-select-tree:4.x
    php artisan filament:assets
    
  2. Basic Usage (Relationship-Based): For a BelongsTo relationship (single selection):

    use CodeWithDennis\FilamentSelectTree\SelectTree;
    
    SelectTree::make('category_id')
        ->relationship('category', 'name', 'parent_id');
    

    For a BelongsToMany relationship (multi-select):

    SelectTree::make('categories')
        ->relationship('categories', 'name', 'parent_id');
    
  3. Non-Relationship Usage:

    SelectTree::make('category_id')
        ->query(fn() => Category::query(), 'name', 'parent_id');
    

First Use Case

Replace a standard Select or MultiSelect field in a Filament form/resource with hierarchical data (e.g., product categories). Example:

use Filament\Forms\Components\SelectTree;

SelectTree::make('category_id')
    ->label('Product Category')
    ->relationship('category', 'name', 'parent_id')
    ->required();

Implementation Patterns

Common Workflows

1. Form Integration

  • Single Selection (BelongsTo):
    SelectTree::make('parent_category_id')
        ->relationship('parentCategory', 'title', 'parent_id')
        ->searchable()
        ->placeholder('Select a parent category');
    
  • Multi-Selection (BelongsToMany):
    SelectTree::make('categories')
        ->relationship('categories', 'name', 'parent_id')
        ->multiple()
        ->enableBranchNode()
        ->withCount();
    

2. Table Filters

Filters\Filter::make('category_filter')
    ->form([
        SelectTree::make('categories')
            ->relationship('categories', 'name', 'parent_id')
            ->independent(false)
            ->enableBranchNode(),
    ])
    ->query(function (Builder $query, array $data) {
        return $query->when($data['categories'], fn($q) =>
            $q->whereHas('categories', fn($q) => $q->whereIn('id', $data['categories']))
        );
    });

3. Dynamic Tree Data

Use getTreeUsing for custom tree structures:

SelectTree::make('custom_tree')
    ->getTreeUsing(function () {
        return Category::with('children')
            ->get()
            ->map(fn($category) => [
                'name' => $category->name,
                'value' => $category->id,
                'children' => $category->children->map(fn($child) => [
                    'name' => $child->name,
                    'value' => $child->id,
                ])->toArray(),
            ]);
    });

4. Prepend/Append Static Nodes

Add custom options to the tree (e.g., "All Categories"):

SelectTree::make('categories')
    ->relationship('categories', 'name', 'parent_id')
    ->prepend([
        'name' => 'All Categories',
        'value' => 'all',
        'parent' => null,
    ]);

Integration Tips

  • Dependency Management: Use independent(false) for dependent fields (e.g., child categories depend on parent selection).
  • Performance: For large datasets, use storeResults() to cache query results and withTrashed() to include soft-deleted items if needed.
  • Localization: Translate labels dynamically:
    ->placeholder(__('Select an option'))
    ->emptyLabel(__('No results found'))
    

Gotchas and Tips

Pitfalls

  1. Strict Parent Filtering:

    • By default, if a parent node is filtered out, its children are promoted to root nodes. Disable this with:
      ->strictNullParentRootNodes();
      
    • Debug Tip: Check if parent_id values align with your database schema (e.g., null vs. -1 via parentNullValue(-1)).
  2. Closure Evaluation Timing:

    • Closures in prepend()/append() are deferred. Ensure they return the expected structure:
      ->prepend(fn() => ['name' => 'Dynamic Option', 'value' => 1])
      
  3. Key Mismatches:

    • If using custom keys (e.g., withKey('code')), ensure the stored value matches the relationship’s foreign key.
  4. Search Behavior:

    • Search applies to all levels by default. For level-specific search, use getTreeUsing with custom logic.

Debugging

  • Empty Tree: Verify the query returns data. Use dd() in the closure to inspect results:
    ->query(fn() => dd(Category::query()->get()))
    
  • Selection Issues: Check if multiple() is set correctly for BelongsToMany relationships.
  • UI Glitches: Clear Filament cache (php artisan filament:cache-clear) if styles behave unexpectedly.

Extension Points

  1. Custom Tree Logic: Override tree generation with getTreeUsing for complex hierarchies (e.g., multi-tenancy):

    ->getTreeUsing(function () {
        return auth()->user()->categories->toTreeArray();
    });
    
  2. Dynamic Disabled/Hidden Options: Use disabledOptions/hiddenOptions with stored results:

    ->storeResults()
    ->disabledOptions(fn($state, $component) => [
        $component->getResults()->where('is_active', false)->pluck('id')
    ]);
    
  3. Event Listeners: Attach JavaScript events (e.g., x-load) for dynamic updates:

    ->extraAttributes(['x-load' => 'loadTreeData'])
    

Pro Tips

  • Default Expand Level: Use defaultOpenLevel(2) to auto-expand the tree to a specific depth.
  • Grouped Values: For cleaner UX, enable isGroupedValue() to return parent IDs when all children are selected.
  • Static Lists: Use staticList() to prevent dropdown overlap in dense forms.
  • Direction Control: Force dropdown direction (top/bottom) for consistent placement:
    ->direction('top')
    
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.
besmartand-pro/php-quality-config
sentix/ai-chatbot
terminal42/code-quality-tools
codifyo/ts-generator-bundle
testo/fiber
mintobit/jobqueue
a4sex/maintenance-bundle
a4sex/entity-date-update
a4sex/client-identifier
a4sex/base-utilites
a4sex/key-value-storage
a4sex/micro-status
chilldev/dependency-injection-extra
datinglibre/datinglibre-app-api
biberltd/corebundle
bricre/symfony-bundle-test
biberltd/logbundle
dominium/http-adapter-bundle
dominium/google-analytics
a4sex/auto-clean-entity