Features System
The features system controls tenant access to platform capabilities. It supports both boolean feature flags (on/off) and numeric limits (quotas). Features can be granted at the plan level or customized per-tenant.
Data Model
Three tables define the features system:
| Table | Purpose |
|---|---|
pla_features | Feature definitions (slug, type, attributes) |
pla_plan_features | Features included in each plan |
pla_tenant_features | Per-tenant customizations (overrides) |
The flow is: Feature Definition → Plan Assignment → Tenant Override
Feature Types
Boolean Features
Control access to capabilities. Examples: pwa, automations, storefront_search.
- Stored as
'enabled'or'disabled'in DB - Runtime value:
'enabled'or'disabled'(usePlaFeature::VALUE_ENABLED/VALUE_DISABLED) - Identified by
has_value = falseinpla_features
Limit Features
Define numeric quotas. Examples: admin_limit, issues_limit, maximum_discount_coupon_amount_limit.
- Stored as numeric string or
'unlimited'in DB - Runtime value: integer or
'unlimited'(usePlaFeature::VALUE_UNLIMITED) - Identified by
has_value = trueinpla_features
Value Semantics
Runtime values use semantic strings from PlaFeature::VALUE_* constants:
| DB Value | Feature Type | Runtime Value | Meaning |
|---|---|---|---|
'enabled' | boolean | VALUE_ENABLED | Feature is on |
'disabled' | boolean | VALUE_DISABLED | Feature is off |
'unlimited' | limit | VALUE_UNLIMITED | No cap |
'500' | limit | 500 (int) | Capped at 500 |
Resolution Priority
When checking feature access, the system evaluates in order (first match wins):
- Deprecating features → always enabled (transitioning to core)
- Forcefully disabled → custom feature with
VALUE_DISABLEDblocks access - Platform features → check global config flags
- Trial period → enabled if
accessible_during_trial = true - Custom tenant feature → enabled if exists and not
VALUE_DISABLED - Plan feature → enabled if exists and not
VALUE_DISABLED - Default → denied
Custom tenant features can both add features not in a plan and disable features that are in a plan (via VALUE_DISABLED).
Feature Attributes
Each feature definition has attributes controlling behavior:
| Attribute | Purpose |
|---|---|
accessible_during_trial | Allow access during trial period |
displayable_publicly | Show on billing/features page |
value_can_be_unlimited | Allow 'unlimited' for limit features |
default_value | Fallback when not explicitly set |
Caching
Feature caching is part of the broader Tenant Resolver system.
Quick overview:
- Features are computed by
TenantResolverUnifiedQueryService::computeFeatures() - Results cached in Redis (700-1000s TTL) as part of the tenant payload
$tenant->available_featuresreturns the cached payload when tenant was loaded via resolver$tenant->resolved_featuresprovides in-memory caching for the current request
Invalidation triggers:
- Plan features change →
PlaPlanFeatureObserver - Tenant features change →
PlaTenantFeatureObserver - Manual flush →
flushTenantCacheById()
Constraints
The FeatureConstraints registry applies validation rules to specific features:
// Example: coupon discount capped at 100
'maximum_discount_coupon_amount_limit' => ['max' => 100]
Constraints are applied automatically when saving PlaPlanFeature or PlaTenantFeature records.
Related Documentation
- Platform Feature Flags (Product) - Business-level feature descriptions, categories, and availability