Filament Nested Resources allows you to create nested resources of any depth. This is useful for resources which are too complex for relation manager, yet don't make sense as a standalone resource.
Your support is key to the continual advancement of our plugin. We appreciate every user who has contributed to our journey so far.
While our plugin is available for all to use, if you are utilizing it for commercial purposes and believe it adds significant value to your business, we kindly ask you to consider supporting us through GitHub Sponsors. This sponsorship will assist us in continuous development and maintenance to keep our plugin robust and up-to-date. Any amount you contribute will greatly help towards reaching our goals. Join us in making this plugin even better and driving further innovation.
You can install the package via composer:
composer require guava/filament-nested-resources
Throughout the documentation I refer to root
nested resource and child
nested resources. The only difference is that the Root
is the first resource in the relationship tree.
In the examples: ArtistResource > AlbumResource > SongResource
Artist would be the root resource, the other would be child resources.
Replace all extends of Resource
, RelationManager
, Page
classes with their corresponding Nested
variants.
So instead of extending Resource
, extend NestedResource
, instead of extending EditRecord
, extend NestedEditRecord
and so on.
Where applicable (in child resources), implement getAncestor
to define the parent of the nested resource.
Optionally remove index
page for child nested resources.
In order to make to set up Nested Resources, you need to do these steps:
- Extend
NestedResource
on your root resource and all child resources you want to nest under your root resource. - On your child
NestedResources
, implement thegetAncestor()
method and return an instance of theAncestor
config. - On your child
NestedResource
pages, extend the correspondingNested[Context]Record
. So instead ofEditRecord
, extendNestedEditRecord
and so on. - Create a
RelationManager
to bind the Resources together. ExtendNestedRelationManager
instead ofRelationManager
.
Let's imagine the scenario from the Showcase screenshots, where we have this schema:
- Artist Model.
- Album Model (Belongs to Artist).
- Song Model (Belongs to Album).
First we create ArtistResource
:
use Guava\Filament\NestedResources\Resources\NestedResource;
class ArtistResource extends NestedResource
{
//
public static function getRelations(): array
{
return [
AlbumsRelationManager::class,
];
}
}
Next, we create the AlbumResource
:
use Guava\Filament\NestedResources\Resources\NestedResource;
use Guava\Filament\NestedResources\Ancestor;
class AlbumResource extends NestedResource
{
//
public static function getRelations(): array
{
return [
// Repeat the same for Song Resource
];
}
public static function getAncestor() : ?Ancestor
{
// This is just a simple configuration with a few helper methods
return Ancestor::make(
ArtistResource::class, // Parent Resource Class
// Optionally you can pass a relationship name, if it's non-standard. The plugin will try to guess it otherwise
);
}
}
For each page for our AlbumResource
, we extend the corresponding NestedPage:
use Guava\Filament\NestedResources\Pages\NestedCreateRecord;
class CreateAlbum extends NestedCreateRecord
{
//
}
use Guava\Filament\NestedResources\Pages\NestedEditRecord;
class EditAlbum extends NestedEditRecord
{
//
}
use Guava\Filament\NestedResources\Pages\NestedListRecords;
class ListAlbums extends NestedListRecords
{
//
}
And finally we create the AlbumsRelationManager
. We just need to extend the NestedRelationManager
class here:
use Guava\Filament\NestedResources\RelationManagers\NestedRelationManager;
class AlbumsRelationManager extends NestedRelationManager
{
//
}
Optionally, we recommend deleting the index
page from your child NestedResources
(in this case AlbumResource):
public static function getPages(): array
{
return [
'create' => Pages\CreateAlbum::route('/create'),
'edit' => Pages\EditAlbum::route('/{record}/edit'),
];
}
The plugin will work either way, but the difference is that the breadcrumb URLs will redirect to different pages based on whether the index page exists or not.
If it exists, it will redirect to the index page (which might be a little confusing) and if it does NOT exist, it will redirect to the Parent Resource's Edit page. Since there's a relation manager, you will still have a list of all records.
By default, the breadcrumbs will display the ID of the record(s). When recordTitleAttribute
is set, the breadcrumbs will display that attribute instead.
To customize only the breadcrumb attribute, you can override the breadcrumbTitleAttribute
on your NestedResource
:
So for example to display the name
of each record in the breadcrumb:
protected static ?string $breadcrumbTitleAttribute = 'name';
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.