A Filament package to manage adjacency lists (aka trees).
You can install the package via composer:
composer require saade/filament-adjacency-list
[!IMPORTANT] If you have not set up a custom theme and are using Filament Panels follow the instructions in the Filament Docs first.
After setting up a custom theme add the plugin's views to your theme css file or your app's css file if using the standalone packages.
@source '../../../../vendor/saade/filament-adjacency-list/resources/views/**/*.blade.php';
use Saade\FilamentAdjacencyList\Forms\Components\AdjacencyList;Â AdjacencyList::make('subjects') ->form([ Forms\Components\TextInput::make('label') ->required(), ])
label
key used to display the item's labelAdjacencyList::make('subjects') ->labelKey('name') // defaults to 'label'
children
key used to gather the item's children.Note: This is only used when not using relationships.
AdjacencyList::make('subjects') ->childrenKey('children') // defaults to 'children'
MaxDepth
of the tree.AdjacencyList::make('subjects') ->maxDepth(2) // defaults to -1 (unlimited depth)
AdjacencyList::make('subjects') ->itemAction('edit') // or view, delete, moveUp, indent etc ... ->itemUrl( fn (array $item) => YourResource::getUrl('view', ['record' => $item['id']]) // for example )
AdjacencyList::make('subjects') ->itemLabel( fn (array $item): ?string => Page::find($item['data']['id'], ['name'])?->name) )
AdjacencyList::make('subjects') ->modal(false) // defaults to true
AdjacencyList::make('subjects') ->addable(false) ->editable(false) ->deletable(false) ->reorderable(false) ->moveable(false) ->indentable(false)
use Filament\Forms\Actions\Action;Â AdjacencyList::make('subjects') ->addAction(fn (Action $action): Action => $action->icon('heroicon-o-plus')->color('primary')) ->addChildAction(fn (Action $action): Action => $action->button()) ->editAction(fn (Action $action): Action => $action->icon('heroicon-o-pencil')) ->deleteAction(fn (Action $action): Action => $action->requiresConfirmation()) ->reorderAction(fn (Action $action): Action => $action->icon('heroicon-o-arrow-path-rounded-square'))
In this example, we'll be creating a Ticketing system, where tickets can be assigned to a department, and departments have subjects.
// App/Models/Department.php class Department extends Model{ public function subjects(): HasMany { return $this->hasMany(Subject::class)->whereNull('parent_id')->with('children')->orderBy('sort'); }}
// App/Models/Subject.php class Subject extends Model{ protected $fillable ['parent_id', 'name', 'sort']; // or whatever your columns are public function children(): HasMany { return $this->hasMany(Subject::class, 'parent_id')->with('children')->orderBy('sort'); }}
Now you've created a nested relationship between departments and subjects.
// App/Filament/Resources/DepartmentResource.php AdjacencyList::make('subjects') ->relationship('subjects') // Define the relationship ->labelKey('name') // Customize the label key to your model's column ->childrenKey('children') // Customize the children key to the relationship's method name ->form([ // Define the form Forms\Components\TextInput::make('name') ->label(__('Name')) ->required(), ]);
That's it! Now you're able to manage your adjacency lists using relationships.
This package also supports Staudenmeir's Laravel Adjacency List package.
First, install the package:
composer require staudenmeir/laravel-adjacency-list:"^1.0"
HasRecursiveRelationships
trait in your model, and override the default path separator.// App/Models/Department.php class Department extends Model{ use \Staudenmeir\LaravelAdjacencyList\Eloquent\HasRecursiveRelationships; public function getPathSeparator() { return '.children.'; }}
If you're already using the HasRecursiveRelationships trait for other parts of your application, it's probably not a good idea to change your model's path separator, since it can break other parts of your application. Instead, you can add as many path separators as you want:
class Department extends Model{ use \Staudenmeir\LaravelAdjacencyList\Eloquent\HasRecursiveRelationships;Â public function getCustomPaths() { return [ [ 'name' => 'tree_path', 'column' => 'id', 'separator' => '.children.', ], ]; }}
relationship
method to define the relationship:AdjacencyList::make('subdepartments') ->relationship('descendants') // or 'descendantsAndSelf', 'children' ... ->customPath('tree_path') // if you're using custom paths
That's it! Now you're able to manage your adjacency lists using relationships.
AdjacencyList::make('subdepartments') ->relationship('descendants', fn (Builder $query): Builder => $query->where('enabled', 1))
If your application needs to order the items in the list, you can use the orderColumn
method:
AdjacencyList::make('subdepartments') ->orderColumn('sort') // or any other column
The AdjacencyListWidget
can be used to render a tree for any model with a recursive child relationship (including many-to-many graph relationships, using Staudenmeir's HasGraphRelationships trait).
The simplest use case is ...
class DepartmentTreeWidget extends AdjacencyListWidget{ protected static string $relationshipName = 'descendantsAndSelf';Â // If you're using a widget on an Edit or View page, the plugin will automatically set the $record for you. // However, you're free to override this method to customize the record. public function getModel(): Model | string | null { return Department::query()->where(['is_root' => true])->first(); }Â // You can configure the widget using the same methods as the form component. protected function adjacencyList(AdjacencyList $adjacencyList): AdjacencyList { return $adjacencyList ->label('Foo') ->editable() ->labelKey('nombre') ->childrenKey('hijos'); }}
Please see CHANGELOG for more information on what has changed recently.
Please see CONTRIBUTING for details.
Please review our security policy on how to report security vulnerabilities.
The MIT License (MIT). Please see License File for more information.
CTO & Backend Engineer at a Brazilian startup. Creating top-tier technology for public sector applications, making information and services universally accessible.