codewithdennis/filament-select-tree
Installation:
composer require codewithdennis/filament-select-tree:4.x
php artisan filament:assets
Basic Usage (Relationship-Based):
For a BelongsTo relationship:
use CodeWithDennis\FilamentSelectTree\SelectTree;
SelectTree::make('category_id')
->relationship('category', 'name', 'parent_id')
For a BelongsToMany relationship:
SelectTree::make('categories')
->relationship('categories', 'name', 'parent_id')
Non-Relationship Usage:
SelectTree::make('category_id')
->query(fn() => Category::query(), 'name', 'parent_id')
Scenario: Display a hierarchical category selection in a Filament form.
use App\Models\Category;
SelectTree::make('category_id')
->label('Select Category')
->relationship('category', 'name', 'parent_id')
->searchable()
->required();
SelectTree::make('parent_category_id')
->relationship('parentCategory', 'name', 'parent_id')
->label('Parent Category')
->required();
SelectTree::make('subcategories')
->relationship('subcategories', 'name', 'parent_id')
->label('Subcategories')
->multiple()
->enableBranchNode();
SelectTree::make('categories')
->relationship('categories', 'name', 'parent_id')
->modifyQueryUsing(fn($query) => $query->where('is_active', true));
Filters\Filter::make('category_filter')
->form([
SelectTree::make('categories')
->relationship('categories', 'name', 'parent_id')
->enableBranchNode()
->independent(false),
])
->query(function (Builder $query, array $data) {
return $query->when($data['categories'], fn($q) =>
$q->whereHas('categories', fn($q) => $q->whereIn('id', $data['categories']))
);
});
SelectTree::make('custom_tree')
->getTreeUsing(function () {
return [
[
'name' => 'Electronics',
'value' => 'electronics',
'children' => [
['name' => 'Phones', 'value' => 'phones'],
['name' => 'Laptops', 'value' => 'laptops'],
],
],
];
});
SelectTree::make('categories')
->relationship('categories', 'name', 'parent_id')
->prepend([
'name' => 'None',
'value' => null,
'disabled' => true,
]);
parent_id column and a children() relationship method:
public function children()
{
return $this->hasMany(Category::class, 'parent_id');
}
Use storeResults() to cache query results if the tree is static:
SelectTree::make('categories')
->relationship('categories', 'name', 'parent_id')
->storeResults();
Disable search if not needed to reduce client-side processing:
SelectTree::make('categories')
->relationship('categories', 'name', 'parent_id')
->searchable(false);
SelectTree::make('categories')
->relationship('categories', 'name', 'parent_id')
->disabledOptions(function ($state) {
return $state['is_admin'] ? [] : [1, 2]; // Disable options for non-admins
});
Strict Parent Filtering
->strictNullParentRootNodes();
dd($component->getResults()) to inspect the tree structure after filtering.Key Mismatches
code instead of id for relationships, specify the key:
->withKey('code')
Closure Evaluation Timing
prepend()/append() closures are deferred. Ensure dependencies (e.g., $this) are available:
->prepend(fn() => ['name' => $this->getCustomOption(), 'value' => null])
PHP 8.5 Deprecations
null array offsets (e.g., $array[null] = 'value'). Use explicit keys:
->getTreeUsing([
['name' => 'Root', 'value' => 1, 'children' => []],
]);
Search Behavior
getTreeUsing.Inspect Tree Structure
->getTreeUsing() to manually define the tree for debugging:
->getTreeUsing(function () {
$tree = Category::with('children')->get()->toTree();
return $tree->toArray();
});
Check Query Logs
DB::enableQueryLog();
// Use the field...
dd(DB::getQueryLog());
Validate Relationships
// Model
public function categories()
{
return $this->belongsToMany(Category::class);
}
Clear Filament Cache
php artisan filament:cache:clear
Custom Styling
php artisan vendor:publish --tag=filament-select-tree-assets
resources/css/filament-select-tree.scss.Event Listeners
document.addEventListener('filament-select-tree-updated', (event) => {
console.log('Selected values:', event.detail);
});
Custom Validation
SelectTree::make('categories')
->relationship('categories', 'name', 'parent_id')
->rules(['required', 'min:1']);
Localization
->placeholder(__('Select a category'))
->emptyLabel(__('No categories found'))
Access Control
->disabledOptions(function () {
return auth()->user()->is_admin ? [] : [1, 2, 3];
});
How can I help you explore Laravel packages today?