Configuration
| Key | Default | Purpose |
|---|---|---|
scope_model |
App\Models\Team |
the tenant model (admin scope picker, features:list) |
features |
[] |
feature definitions (label, type boolean/limit, default, description, unit, group) |
plans |
[] |
plan definitions (label, description, price, features) |
default_plan |
free |
plan when the resolver returns nothing |
overrides_table |
feature_overrides |
|
connection |
null |
central connection in database-per-tenant apps |
cache.store / cache.ttl |
null / 300 |
cross-request override cache |
upgrade_url |
null |
where locked features send people |
The plugin API
FeaturesPlugin::make()
->features([...])
->plans([...], default: 'free')
->planUsing(fn (?Model $scope) => ...) // string, Plan or null
->usage('projects', fn (?Model $scope) => ...)
->upgradeUrl(fn (?Model $scope) => ...) // or a string
->planPage(MyPlanPage::class) // null to skip
->adminPages() // FeatureFlags + FeatureOverrides
->navigationGroup('Account')->navigationSort(90)
->config(['cache.store' => 'redis']);
Commands
features:list {scope?}— features, defaults, plan values and the resolved value for a scope.features:prune— delete expired overrides.
With Filament Teams
TeamsPlugin::make()->seatLimit(fn (Team $team): ?int => Features::limit('seats', $team));
With Filament Tenancy
Set scope_model to the Tenant model and connection to the central connection; overrides use string scope ids, so Tenancy's string keys work as they are.