Dependencies
Feature dependencies allow you to create relationships between features, ensuring that advanced features are only active when their prerequisites are met.
Defining Dependencies
Section titled “Defining Dependencies”Single Dependency
Section titled “Single Dependency”use Cline\Toggl\Toggl;
Toggl::define('basic-analytics', fn($user) => $user->hasSubscription());
Toggl::define('advanced-analytics') ->requires('basic-analytics') ->resolver(fn($user) => $user->subscription === 'premium');
// With BackedEnumToggl::define(FeatureFlag::AdvancedAnalytics) ->requires(FeatureFlag::BasicAnalytics) ->resolver(fn($user) => $user->subscription === 'premium');If basic-analytics is inactive, advanced-analytics will automatically return false, even if its resolver returns true.
Multiple Dependencies
Section titled “Multiple Dependencies”Toggl::define('team-collaboration') ->requires('user-management', 'real-time-sync') ->resolver(fn($team) => $team->size >= 5);
// With BackedEnum (mixed or all enums)Toggl::define(FeatureFlag::TeamCollaboration) ->requires(FeatureFlag::UserManagement, FeatureFlag::RealtimeSync) ->resolver(fn($team) => $team->size >= 5);All dependencies must be active for the feature to be active.
Checking Dependencies
Section titled “Checking Dependencies”Get Dependencies
Section titled “Get Dependencies”$deps = Toggl::getDependencies('advanced-analytics');// ['basic-analytics']
$deps = Toggl::getDependencies('team-collaboration');// ['user-management', 'real-time-sync']Check if Dependencies are Met
Section titled “Check if Dependencies are Met”if (Toggl::dependenciesMet('advanced-analytics')) { // All dependencies are active}
// With BackedEnumif (Toggl::dependenciesMet(FeatureFlag::AdvancedAnalytics)) { // All dependencies are active}Transitive Dependencies
Section titled “Transitive Dependencies”Dependencies are checked recursively:
Toggl::define('level-1', true);
Toggl::define('level-2') ->requires('level-1') ->resolver(fn() => true);
Toggl::define('level-3') ->requires('level-2') ->resolver(fn() => true);
// level-3 checks: level-2 → level-1Toggl::active('level-3'); // true only if all levels activeCircular Dependency Protection
Section titled “Circular Dependency Protection”Toggl detects and prevents circular dependencies:
Toggl::define('feature-a') ->requires('feature-b') ->resolver(fn() => true);
Toggl::define('feature-b') ->requires('feature-a') ->resolver(fn() => true);
// Both return false - circular dependency detectedToggl::active('feature-a'); // falseToggl::active('feature-b'); // falseUse Cases
Section titled “Use Cases”Feature Tiers
Section titled “Feature Tiers”// Basic → Pro → Enterprise scopeToggl::define('basic-features', fn($user) => $user->hasAnySubscription());
Toggl::define('pro-features') ->requires('basic-features') ->resolver(fn($user) => in_array($user->plan, ['pro', 'enterprise']));
Toggl::define('enterprise-features') ->requires('pro-features') ->resolver(fn($user) => $user->plan === 'enterprise');Progressive Feature Unlocking
Section titled “Progressive Feature Unlocking”// Tutorial completion requiredToggl::define('advanced-tools') ->requires('tutorial-completed') ->resolver(fn($user) => $user->level >= 5);
Toggl::define('expert-mode') ->requires('advanced-tools') ->resolver(fn($user) => $user->level >= 10);Platform Capabilities
Section titled “Platform Capabilities”Toggl::define('offline-mode', fn() => true);
Toggl::define('background-sync') ->requires('offline-mode') ->resolver(fn($device) => $device->hasNetwork());
Toggl::define('real-time-collaboration') ->requires('background-sync') ->resolver(fn($user) => $user->isPremium());API Versioning
Section titled “API Versioning”Toggl::define('api-v1', fn() => true);
Toggl::define('api-v2') ->requires('api-v1') ->resolver(fn($client) => $client->hasOptedIn());
Toggl::define('api-v3') ->requires('api-v2') ->resolver(fn($client) => $client->isEarlyAdopter());Contextual Dependencies
Section titled “Contextual Dependencies”Dependencies work with contextual features:
Toggl::define('team-features', fn($user) => $user->hasTeam());
Toggl::define('team-admin-panel') ->requires('team-features') ->resolver(fn($user) => $user->isTeamAdmin());
// Check for specific userToggl::for($user)->active('team-admin-panel');// Returns true only if:// 1. User has a team (team-features)// 2. User is team admin (team-admin-panel resolver)Combining with Other Features
Section titled “Combining with Other Features”Dependencies + Time Bombs
Section titled “Dependencies + Time Bombs”Toggl::define('base-feature', fn() => true);
Toggl::define('experimental-addon') ->requires('base-feature') ->expiresAfter(days: 30) ->resolver(fn($user) => $user->isBetaTester());Dependencies + Groups
Section titled “Dependencies + Groups”Toggl::defineGroup('advanced-suite', [ 'advanced-analytics', 'custom-reports', 'api-access',]);
// Each feature in group has same dependencyToggl::define('advanced-analytics')->requires('premium-subscription');Toggl::define('custom-reports')->requires('premium-subscription');Toggl::define('api-access')->requires('premium-subscription');
// Activate group only if dependency is metif (Toggl::for($user)->active('premium-subscription')) { Toggl::for($user)->activateGroup('advanced-suite');}Dependencies + Percentage Rollout
Section titled “Dependencies + Percentage Rollout”Toggl::define('new-ui', fn() => true);
// Gradual rollout of advanced features requiring new UIToggl::define('advanced-ui-features') ->requires('new-ui') ->strategy(new PercentageStrategy(25));Manual Override
Section titled “Manual Override”You can still manually activate a feature even if dependencies aren’t met, but it will still check dependencies when evaluated:
Toggl::define('dependency', false);
Toggl::define('dependent') ->requires('dependency') ->resolver(fn() => true);
// Manually activateToggl::activate('dependent');
// Still returns false because dependency not metToggl::active('dependent'); // false
// Activate dependencyToggl::activate('dependency');
// Now it worksToggl::active('dependent'); // trueBest Practices
Section titled “Best Practices”-
Document dependency chains
// Clear scopeToggl::define('level-1', true); // Base levelToggl::define('level-2')->requires('level-1'); // Requires baseToggl::define('level-3')->requires('level-2'); // Requires level 2 -
Avoid deep chains
// ✅ Good - 2-3 levelsbasic → advanced → expert// ❌ Avoid - too complexa → b → c → d → e → f -
Use meaningful names
// ✅ GoodToggl::define('sso-integration')->requires('user-authentication');// ❌ UnclearToggl::define('feature-x')->requires('feature-y'); -
Test dependency chains
test('dependency chain works correctly', function () {Toggl::define('base', false);Toggl::define('dependent')->requires('base');expect(Toggl::active('dependent'))->toBeFalse();Toggl::activate('base');expect(Toggl::active('dependent'))->toBeTrue();});
Next Steps
Section titled “Next Steps”- Variants - A/B testing and value variants
- Advanced Usage - Events, middleware, and commands