Skip to main content

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:

TablePurpose
pla_featuresFeature definitions (slug, type, attributes)
pla_plan_featuresFeatures included in each plan
pla_tenant_featuresPer-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' (use PlaFeature::VALUE_ENABLED / VALUE_DISABLED)
  • Identified by has_value = false in pla_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' (use PlaFeature::VALUE_UNLIMITED)
  • Identified by has_value = true in pla_features

Value Semantics

Runtime values use semantic strings from PlaFeature::VALUE_* constants:

DB ValueFeature TypeRuntime ValueMeaning
'enabled'booleanVALUE_ENABLEDFeature is on
'disabled'booleanVALUE_DISABLEDFeature is off
'unlimited'limitVALUE_UNLIMITEDNo cap
'500'limit500 (int)Capped at 500

Resolution Priority

When checking feature access, the system evaluates in order (first match wins):

  1. Deprecating features → always enabled (transitioning to core)
  2. Forcefully disabled → custom feature with VALUE_DISABLED blocks access
  3. Platform features → check global config flags
  4. Trial period → enabled if accessible_during_trial = true
  5. Custom tenant feature → enabled if exists and not VALUE_DISABLED
  6. Plan feature → enabled if exists and not VALUE_DISABLED
  7. 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:

AttributePurpose
accessible_during_trialAllow access during trial period
displayable_publiclyShow on billing/features page
value_can_be_unlimitedAllow 'unlimited' for limit features
default_valueFallback 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_features returns the cached payload when tenant was loaded via resolver
  • $tenant->resolved_features provides 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.

X

Graph View