A helper package for when creating packages that provide filament resources.
timo-de-winter/filament-modifiable-plugins is a Laravel package for a helper package for when creating packages that provide filament resources..
It currently has 1 GitHub stars and 3.247 downloads on Packagist (latest version v5.6.1).
Install it with composer require timo-de-winter/filament-modifiable-plugins.
Discover more Laravel packages by timo-de-winter
or browse all Laravel packages to compare alternatives.
Last updated
A powerful package for creating highly customizable Filament plugins. This package empowers plugin developers to provide end-users with extensive customization options for resources, including navigation, forms, tables, clusters, and more.
When building Filament plugins that provide resources or pages, you often want to give developers using your plugin the flexibility to customize various aspects such as:
This package provides a clean, fluent API to make all of these customizations possible without requiring plugin users to override entire resource classes.
Install the package via Composer:
composer require timo-de-winter/filament-modifiable-plugins
No additional configuration or publishing is required. The package will auto-register via Laravel's package discovery.
If you've installed a Filament plugin that uses this package, you can easily customize the plugin's resources without modifying the plugin's source code.
Here's a comprehensive example showing all available customization options:
use Filament\Panel;
use YourVendor\YourPlugin\YourPlugin;
use YourVendor\YourPlugin\Resources\UserResource;
use App\Filament\Clusters\Settings;
public function panel(Panel $panel): Panel
{
return $panel
->plugins([
YourPlugin::make()
// Navigation customization
->navigationGroup('Settings', UserResource::class)
->label('System Users', UserResource::class)
->sort(10, UserResource::class)
->icon('heroicon-o-users', UserResource::class)
->activeIcon('heroicon-s-users', UserResource::class)
->badge('New', UserResource::class)
->badgeColor('success', UserResource::class)
->badgeTooltip('Recently added users', UserResource::class)
// Cluster and organization
->cluster(Settings::class, UserResource::class)
->parentItem('user-management', UserResource::class)
// Page customization
->title('Manage Users', UserResource::class)
->slug('system-users', UserResource::class)
// Multi-tenancy
->tenantOwnershipRelationshipName('organization', UserResource::class)
]);
}
Control how resources appear in your Filament panel's navigation:
YourPlugin::make()
->navigationGroup('Settings', UserResource::class)
->label('Users', UserResource::class)
->sort(5, UserResource::class)
->icon('heroicon-o-users', UserResource::class)
->activeIcon('heroicon-s-users', UserResource::class)
->badge('12', UserResource::class)
->badgeColor('warning', UserResource::class)
All navigation methods support closures for dynamic values:
YourPlugin::make()
->badge(fn () => User::count(), UserResource::class)
->badgeColor(fn () => User::count() > 100 ? 'danger' : 'success', UserResource::class)
Customize page titles, slugs, and organization:
YourPlugin::make()
->title('User Management', UserResource::class)
->slug('manage-users', UserResource::class)
->cluster(Settings::class, UserResource::class)
->parentItem('system', UserResource::class)
Customize the form schema used in your resource's create and edit pages:
use Filament\Schemas\Schema;
use Filament\Schemas\Components\TextInput;
use Filament\Schemas\Components\Select;
YourPlugin::make()
->schema(function (Schema $schema) {
return $schema
->components([
TextInput::make('name')
->required()
->maxLength(255),
TextInput::make('email')
->email()
->required(),
Select::make('role')
->options([
'admin' => 'Administrator',
'user' => 'User',
])
->required(),
]);
}, UserResource::class)
Customize table columns, filters, and actions:
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Filters\SelectFilter;
use Filament\Tables\Actions\EditAction;
use Filament\Tables\Actions\DeleteAction;
YourPlugin::make()
// Customize specific table elements
->columns([
TextColumn::make('name')->searchable(),
TextColumn::make('email')->searchable(),
TextColumn::make('created_at')->dateTime(),
], UserResource::class)
->filters([
SelectFilter::make('role')
->options([
'admin' => 'Admin',
'user' => 'User',
]),
], UserResource::class)
->recordActions([
EditAction::make(),
DeleteAction::make(),
], UserResource::class)
->toolbarActions([
// Your toolbar/bulk actions here
], UserResource::class)
Alternatively, override the entire table configuration:
use Filament\Tables\Table;
YourPlugin::make()
->customTable(function (Table $table) {
return $table
->columns([
// Your columns
])
->filters([
// Your filters
])
->recordActions([
// Your actions
])
->toolbarActions([
// Your toolbar actions
]);
}, UserResource::class)
When a plugin provides multiple resources, you can target specific ones by passing the resource class as the second parameter:
use YourVendor\YourPlugin\Resources\UserResource;
use YourVendor\YourPlugin\Resources\RoleResource;
YourPlugin::make()
// Customize UserResource
->navigationGroup('User Management', UserResource::class)
->icon('heroicon-o-users', UserResource::class)
// Customize RoleResource
->navigationGroup('User Management', RoleResource::class)
->icon('heroicon-o-shield-check', RoleResource::class)
// This applies to all resources without specific customization
->sort(10)
Override the relations and pages for a resource:
use YourVendor\YourPlugin\Resources\UserResource\RelationManagers\RolesRelationManager;
use YourVendor\YourPlugin\Resources\UserResource\Pages;
YourPlugin::make()
->customRelations([
RolesRelationManager::class,
// Your custom relation managers
], UserResource::class)
->customPages([
'index' => Pages\ListUsers::class,
'create' => Pages\CreateUser::class,
'edit' => Pages\EditUser::class,
// Your custom pages
], UserResource::class)
If you're developing a Filament plugin and want to make your resources customizable, follow these steps.
Add the CanModifyResources trait to your plugin class:
<?php
namespace YourVendor\YourPlugin;
use Filament\Contracts\Plugin;
use Filament\Panel;
use TimoDeWinter\FilamentModifiablePlugins\Concerns\CanModifyResources;
class YourPlugin implements Plugin
{
use CanModifyResources;
public function getId(): string
{
return 'your-plugin-id';
}
public function register(Panel $panel): void
{
$panel->resources([
Resources\UserResource::class,
Resources\RoleResource::class,
]);
}
public function boot(Panel $panel): void
{
//
}
public static function make(): static
{
return app(static::class);
}
}
Add the CanBeModified trait to each resource you want to make customizable:
<?php
namespace YourVendor\YourPlugin\Resources;
use Filament\Resources\Resource;
use TimoDeWinter\FilamentModifiablePlugins\Concerns\CanBeModified;
class UserResource extends Resource
{
use CanBeModified;
protected static ?string $model = User::class;
protected static ?string $navigationIcon = 'heroicon-o-users';
// Your resource implementation...
}
Use the getCustomSchema() method to allow form customization:
use Filament\Schemas\Schema;
use Filament\Schemas\Components\TextInput;
public static function schema(Schema $schema): Schema
{
return self::getCustomSchema($schema, function (Schema $schema) {
return $schema
->components([
TextInput::make('name')
->required()
->maxLength(255),
TextInput::make('email')
->email()
->required(),
// Your default schema components...
]);
});
}
Use the getCustomTable() method with CustomizableTable for table customization:
use Filament\Tables\Table;
use TimoDeWinter\FilamentModifiablePlugins\CustomizableTable;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Filters\TrashedFilter;
use Filament\Tables\Actions\EditAction;
use Filament\Tables\Actions\DeleteAction;
public static function table(Table $table): Table
{
return self::getCustomTable($table, function (CustomizableTable $table) {
return $table
->defaultColumns([
TextColumn::make('id')->sortable(),
TextColumn::make('name')->searchable()->sortable(),
TextColumn::make('email')->searchable(),
TextColumn::make('created_at')->dateTime(),
])
->defaultFilters([
TrashedFilter::make(),
])
->defaultRecordActions([
EditAction::make(),
DeleteAction::make(),
])
->defaultToolbarActions([
// Your default toolbar actions
]);
});
}
Use the getCustomRelations() method:
public static function getRelations(): array
{
return self::getCustomRelations([
RelationManagers\RolesRelationManager::class,
// Your default relation managers...
]);
}
Use the getCustomPages() method:
public static function getPages(): array
{
return self::getCustomPages([
'index' => Pages\ListUsers::class,
'create' => Pages\CreateUser::class,
'edit' => Pages\EditUser::class,
// Your default pages...
]);
}
Always provide sensible defaults - Your resources should work perfectly out of the box even if users don't customize them.
Use the helper methods consistently - Always use getCustomSchema(), getCustomTable(), etc., in your resources to ensure customizations are applied.
Document customization options - Let your users know which resources can be customized and provide examples in your plugin's documentation.
Test with customizations - Include tests that verify customizations work correctly.
Use type hints - The package fully supports IDE autocompletion and type checking.
Here's a complete example of a properly implemented modifiable resource:
<?php
namespace YourVendor\YourPlugin\Resources;
use Filament\Resources\Resource;
use Filament\Schemas\Schema;
use Filament\Schemas\Components\TextInput;
use Filament\Tables\Table;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Actions\EditAction;
use Filament\Tables\Actions\DeleteAction;
use TimoDeWinter\FilamentModifiablePlugins\Concerns\CanBeModified;
use TimoDeWinter\FilamentModifiablePlugins\CustomizableTable;
use YourVendor\YourPlugin\Models\User;
class UserResource extends Resource
{
use CanBeModified;
protected static ?string $model = User::class;
protected static ?string $navigationIcon = 'heroicon-o-users';
protected static ?string $navigationGroup = 'Users';
public static function schema(Schema $schema): Schema
{
return self::getCustomSchema($schema, function (Schema $schema) {
return $schema
->components([
TextInput::make('name')
->required()
->maxLength(255),
TextInput::make('email')
->email()
->required()
->unique(ignoreRecord: true),
]);
});
}
public static function table(Table $table): Table
{
return self::getCustomTable($table, function (CustomizableTable $table) {
return $table
->defaultColumns([
TextColumn::make('id')->sortable(),
TextColumn::make('name')->searchable()->sortable(),
TextColumn::make('email')->searchable(),
TextColumn::make('created_at')->dateTime()->sortable(),
])
->defaultFilters([
//
])
->defaultRecordActions([
EditAction::make(),
DeleteAction::make(),
])
->defaultToolbarActions([
//
]);
});
}
public static function getRelations(): array
{
return self::getCustomRelations([
//
]);
}
public static function getPages(): array
{
return self::getCustomPages([
'index' => Pages\ListUsers::class,
'create' => Pages\CreateUsers::class,
'edit' => Pages\EditUsers::class,
]);
}
}
| Method | Description |
|--------|-------------|
| navigationGroup($group, $resource) | Set the navigation group |
| label($label, $resource) | Set the navigation label |
| sort($order, $resource) | Set the navigation sort order |
| icon($icon, $resource) | Set the navigation icon |
| activeIcon($icon, $resource) | Set the active navigation icon |
| badge($badge, $resource) | Set the navigation badge |
| badgeColor($color, $resource) | Set the navigation badge color |
| badgeTooltip($tooltip, $resource) | Set the navigation badge tooltip |
| parentItem($parent, $resource) | Set the navigation parent item |
| cluster($cluster, $resource) | Set the resource cluster |
| title($title, $resource) | Set the page title |
| slug($slug, $resource) | Set the resource slug |
| tenantOwnershipRelationshipName($name, $resource) | Set tenant ownership relationship |
| schema($callback, $resource) | Customize the form schema |
| columns($columns, $resource) | Set table columns |
| filters($filters, $resource) | Set table filters |
| recordActions($actions, $resource) | Set table record actions |
| toolbarActions($actions, $resource) | Set table toolbar actions |
| customTable($callback, $resource) | Override entire table configuration |
| customRelations($relations, $resource) | Set custom relation managers |
| customPages($pages, $resource) | Set custom pages |
All methods support closures for dynamic values and accept an optional resource class as the second parameter.
| Method | Description |
|--------|-------------|
| getCustomSchema($schema, $default) | Get customized schema or default |
| getCustomTable($table, $default) | Get customized table or default |
| getCustomRelations($default) | Get customized relations or default |
| getCustomPages($default) | Get customized pages or default |
Run the test suite:
composer test
Run tests with coverage:
composer test-coverage
Fix code style issues:
composer format
Please see CHANGELOG for more information on what has changed recently.
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
Please make sure to update tests as appropriate.
If you discover a security vulnerability within this package, please send an e-mail to Timo de Winter via [email protected]. All security vulnerabilities will be promptly addressed.
Please review our security policy on how to report security vulnerabilities.
The MIT License (MIT). Please see License File for more information.
If you find this package helpful, please consider:
For issues, questions, or suggestions, please use the GitHub issue tracker.