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.
## Getting Started
### Minimal Setup
1. **Installation**:
```bash
composer require openplain/filament-tree-view
Publish the config (if needed):
php artisan vendor:publish --provider="Openplain\FilamentTreeView\FilamentTreeViewServiceProvider"
Basic Integration:
Replace your Table with TreeView in a Filament resource:
use Openplain\FilamentTreeView\Columns\TreeViewColumn;
use Openplain\FilamentTreeView\TreeView;
public static function table(Table $table): Table
{
return TreeView::make()
->columns([
TreeViewColumn::make('name'),
// Other columns...
])
->query($this->query());
}
First Use Case: Display a category hierarchy with drag-and-drop:
TreeView::make()
->columns([
TreeViewColumn::make('title'),
Columns\TextColumn::make('description'),
])
->query(Category::query())
->parentColumn('parent_id') // Required
->orderColumn('order_column') // Optional
->bulkActions([...]); // Optional
parentColumn (foreign key to parent) and orderColumn (for sorting).
depthColumn, iconColumn, or indentation for visual hierarchy.// Define parent-child relationships (now works with non-integer keys)
TreeView::make()
->parentColumn('parent_id') // Supports UUIDs, strings, etc.
->orderColumn('sort_order')
->query(Post::query());
// Customize indentation (default: 20px)
->indentation(30);
Enable with:
->enableDragAndDrop()
->onReorder(function (array $items) {
// Handle reorder logic (e.g., update `order_column`)
Post::whereIn('id', array_keys($items))
->update(['sort_order' => array_values($items)]);
});
->bulkActions([
Tables\Actions\DeleteBulkAction::make(),
Tables\Actions\ForceDeleteBulkAction::make(),
]);
TreeViewColumn::make('title')
->toggleable(isToggledHiddenByDefault: true)
->searchable();
->actions([
Tables\Actions\EditAction::make(),
Tables\Actions\ViewAction::make(),
function (Post $record) {
return Action::make('Custom')
->icon('heroicon-o-pencil')
->url(fn () => route('posts.edit', $record));
},
]);
// Example with UUIDs
TreeView::make()
->parentColumn('parent_uuid') // String-based parent key
->orderColumn('sort_order')
->query(Post::query());
// Assuming `parent_id` (now supports non-integer) and `lft`, `rgt` columns
TreeView::make()
->parentColumn('parent_id')
->orderColumn('lft') // Use left/right for nested sets
->query(Post::query());
->query(fn () => Post::where('published', true)->with('author'))
->searchable(['title', 'content']);
->columns([
TreeViewColumn::make('title')
->visible(fn ($record) => $record->is_published),
]);
->headerActions([
Header::make()->slot([
Button::make('Refresh')
->icon('heroicon-o-arrow-path')
->action(fn () => $this->refresh()),
]),
]);
Performance with Large Trees
->limitDepth(3) or lazy-load children via AJAX.Drag-and-Drop Conflicts
Sortable).data-js attributes in your Blade templates.Order Column Misconfiguration
orderColumn throws RuntimeException.orderColumn (even if unused for display).Circular References
parent_id pointing to itself).public function setParentIdAttribute($value) {
if ($value == $this->id) {
throw new \Exception("Cannot be its own parent.");
}
$this->attributes['parent_id'] = $value;
}
Non-Integer Primary Key Issues (New in v0.5.6)
parentColumn uses the correct key type (e.g., parent_uuid for UUIDs).Check Tree Structure Use Tinker to verify your hierarchy:
php artisan tinker
>>> Post::with('children')->find(1)->toTree();
Log Reorder Events
->onReorder(function (array $items) {
\Log::info('Reordered items:', $items);
// Update logic...
});
Disable JavaScript
Livewire Modal Debugging
TreeView component is properly mounted in the modal.Use toTree() for Testing
Leverage staudenmeir/eloquent-nestedset or spatie/laravel-activitylog to debug hierarchies:
Post::find(1)->toTree();
Custom Icons for Depth
->columns([
TreeViewColumn::make('title')
->icon(fn ($record) => match ($record->depth) {
0 => 'heroicon-o-home',
1 => 'heroicon-o-folder',
default => 'heroicon-o-document',
}),
]);
Optimize for Mobile
->enableCollapseAll()
->enableExpandAll();
Extend with Custom Columns Create a custom column class:
namespace App\Filament\Columns;
use Openplain\FilamentTreeView\Columns\TreeViewColumn;
class CustomTreeViewColumn extends TreeViewColumn {
protected string $view = 'filament-tree-view::columns.custom';
}
Cache Tree Queries For read-heavy apps, cache the tree structure:
->query(fn () => cache()->remember('tree-data', now()->addHours(1), function () {
return Post::query();
}));
Handle Soft Deletes Ensure your query respects soft deletes:
->query(Post::withTrashed()->query());
Localization Override labels in your Filament config:
'filament-tree-view' => [
'tree' => [
'actions' => [
'expand_all' => 'Expand All',
'collapse_all' => 'Collapse All',
],
],
],
UUID/Non-Integer Key Workflow
use Illuminate\Database\Eloquent\Concerns\HasUuids;
class Post extends Model {
use HasUuids;
// ...
}
TreeView::make()
->parentColumn('
How can I help you explore Laravel packages today?