Install the Package
composer require wsmallnews/filament-nestedset:^2.0
Publish config (optional):
php artisan vendor:publish --tag="sn-filament-nestedset-config"
Prepare Your Model
NodeTrait to your Eloquent model:
use Kalnoy\Nestedset\NodeTrait;
class YourModel extends Model
{
use NodeTrait;
}
php artisan migrate
Generate a NestedSet Page
php artisan make:filament-nestedset-page
Test):
protected static string $recordTitleAttribute = 'name'; // Replace with your model's title field
protected static string $model = YourModel::class;
Define Form Schema
Override schema(), createSchema(), or editSchema() in your page class:
protected function schema(array $arguments): array
{
return [
Forms\Components\TextInput::make('name')->required(),
];
}
Register the Page
Add it to your Filament admin panel in AppServiceProvider:
Filament::registerPages([
\App\Filament\Pages\Test::class,
]);
Tree Visualization
kalnoy/nestedset).$recordTitleAttribute to control the displayed label for nodes.getRecordLabel():
public function getRecordLabel(Model $item): string
{
return $item->custom_title ?? $item->name;
}
Parent-Child Relationships
protected static bool $create_action_modal_show_parent_select = true;
protected static bool $show_create_child_node_action_in_row = true;
ParentSelect field uses codewithdennis/filament-select-tree.Data Organization
type):
protected static ?string $tabFieldName = 'type';
public function getTabs(): array
{
return [
'web' => Tab::make()->label('Website'),
'shop' => Tab::make()->label('Shop'),
];
}
Ensure your model’s getScopeAttributes() includes the tab field:
public function getScopeAttributes(): array
{
return ['type'];
}
Multi-Tenancy
protected static bool $isScopedToTenant = false;
getScopeAttributes() in your model:
public function getScopeAttributes(): array
{
return ['team_id'];
}
Level Limits
protected static ?int $level = 3; // Max 3 levels deep
public function getLevel(): ?int
{
return auth()->user()->can('deep_nesting') ? 5 : 3;
}
Additional Scoping
nestedScoped():
public function nestedScoped()
{
return ['category_id' => request('category')];
}
getScopeAttributes() in your model.Info List Customization
protected function infolistSchema(): array
{
return [
TextEntry::make('created_at')->dateTime(),
Badge::make('status')->color(fn ($record) => $record->status === 'active' ? 'success' : 'danger'),
];
}
protected static string $infolistHiddenEndpoint = 'lg'; // Hidden on lg and below
protected static Alignment $infolistAlignment = Alignment::Left;
Styling
'autoload_assets' => false,
php artisan vendor:publish --tag="sn-filament-nestedset-views"
AI Guidelines (Laravel Boost)
php artisan boost:update --discover
boost.json and CLAUDE.md with usage context.Event Handling
// In your Filament page's JS
document.addEventListener('sn-filament-nestedset-leaf-click', (e) => {
console.log('Clicked node:', e.detail.node);
});
Performance
public function getEloquentQuery($query)
{
return $query->with(['children' => function ($q) {
$q->limit(50); // Load first 50 children
}]);
}
Testing
$this->filament()->actingAs($user)
->withinPage(NestedsetPage::class)
->assertSeeInTree('Parent Node')
->click('Create Child')
->assertSee('Child Node');
Migration Conflicts
nestedSet() to an existing table, ensure no data conflicts with the new columns (lft, rgt, depth).Parent-Child Circular References
createSchema():
Forms\Components\Select::make('parent_id')
->rules(['nullable', function ($attribute, $value, $fail) {
if ($value && $this->record->isDescendantOf($value)) {
$fail('Cannot create a child of a descendant.');
}
}])
Tab Scoping Overrides
getScopeAttributes() includes both the tab field and tenant field:
public function getScopeAttributes(): array
{
return ['type', 'team_id']; // Both fields must be scoped
}
Level Limits and Depth
static::$level to 3 allows depths up to 3, not exactly 3. Depth 0 is root.getLevel() for dynamic limits (e.g., based on user roles).Asset Loading Conflicts
autoload_assets is true but your Filament theme overrides styles, the tree may render incorrectly.'autoload_assets' => false and manually include the CSS in your layout:
<link href="{{ asset('vendor/filament-nestedset/css/nestedset.css') }}" rel="stylesheet">
Empty State Customization
$emptyLabel and $emptyTipLabel, but these are not translated by default.php artisan vendor:publish --tag="sn-filament-nestedset-translations"
Then add translations to resources/lang/{locale}/filament-nestedset.php.Livewire Event Scope
sn-filament-nestedset-leaf-click are scoped to the Livewire component. Ensure your JS listener is attached to the correct element:
document.querySelector('[wire\\:id^="test-page"]').addEventListener('sn-filament-nestedset-leaf-click', ...);
Multi-Tenancy Without Tenant Column
team_id column but Filament uses multi-tenancy, the package will throw an error.How can I help you explore Laravel packages today?