Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Feature Flags Laravel Package

ylsideas/feature-flags

Extensible feature flags for Laravel to safely toggle code and features. Manage flags in application logic, routes, Blade views, scheduler tasks, and validation rules to support continuous integration and controlled rollouts.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require ylsideas/feature-flags:^3.0
    php artisan vendor:publish --provider="YlsIdeas\FeatureFlags\FeatureFlagsServiceProvider" --tag=config
    
    • Verify config/features.php is published and configured (default uses in_memory driver).
  2. First Check:

    use YlsIdeas\FeatureFlags\Facades\Features;
    
    // Check if a feature is enabled
    if (Features::accessible('new-dashboard')) {
        // Feature is enabled
    }
    
  3. Define Flags:

    • Use the Features::enable() method in a service provider or migration:
      Features::enable('new-dashboard');
      
    • For environment-specific flags, use the env() helper in the config file:
      'flags' => [
          'new-dashboard' => env('FEATURE_NEW_DASHBOARD', false),
      ],
      

First Use Case: Route Protection

Protect a route from being accessed if a feature is disabled:

Route::get('/admin/dashboard', function () {
    return view('dashboard');
})->middleware('feature:new-dashboard');
  • Register the middleware in app/Http/Kernel.php:
    protected $routeMiddleware = [
        'feature' => \YlsIdeas\FeatureFlags\Http\Middleware\FeatureMiddleware::class,
    ];
    

Implementation Patterns

1. Conditional Logic in Code

Use Features::accessible() for runtime checks:

if (Features::accessible('experimental-api')) {
    $response = $this->callExperimentalApi();
} else {
    $response = $this->fallbackApi();
}

2. Query Builder Integration

Filter Eloquent queries dynamically:

$activeUsers = User::whenFeatureIsAccessible('premium-features')
    ->where('subscription', 'premium')
    ->get();

3. Task Scheduling

Skip scheduled tasks if a feature is disabled:

$schedule->command('send-promotional-emails')->when(Features::accessible('promo-campaign'));

4. Validation Rules

Add feature-dependent validation:

$request->validate([
    'premium_feature' => 'required_if:feature.enabled,new-premium-feature',
]);
  • Register the rule in AppServiceProvider:
    Validator::extend('feature', function ($attribute, $value, $parameters, $validator) {
        return Features::accessible($parameters[0]);
    });
    

5. Blade Directives

Hide/show Blade sections:

@feature('dark-mode')
    <div class="dark-theme">...</div>
@endfeature
  • Register the directive in AppServiceProvider:
    Blade::directive('feature', function ($expression) {
        return "<?php if (\\YlsIdeas\\FeatureFlags\\Facades\\Features::accessible({$expression})): ?>";
    });
    Blade::directive('endfeature', function () {
        return "<?php endif; ?>";
    });
    

6. Testing Features

Mock flags in tests:

Features::fake(['new-dashboard' => true]);

// Test logic that depends on the flag
$this->assertTrue(Features::accessible('new-dashboard'));

// Reset fakes
Features::fake();

7. Driver Customization

Extend the pipeline with custom drivers (e.g., database, Redis):

// config/features.php
'drivers' => [
    \YlsIdeas\FeatureFlags\Drivers\DatabaseDriver::class,
    \YlsIdeas\FeatureFlags\Drivers\CacheDriver::class,
],

Gotchas and Tips

Pitfalls

  1. Caching Issues:

    • Flags cached via CacheDriver may not reflect real-time changes. Use Features::refresh() or clear cache manually:
      php artisan cache:clear
      
    • Fix: Configure cache_ttl in features.php (e.g., 60 seconds for testing).
  2. Middleware Misconfiguration:

    • Forgetting to register the FeatureMiddleware in app/Http/Kernel.php will silently fail.
    • Tip: Use php artisan route:list to verify middleware is applied.
  3. Boolean Casting:

    • Older versions (pre-3.0.1) might return non-boolean values from cache. Explicitly cast:
      $isAccessible = (bool) Features::accessible('flag-name');
      
  4. Environment Overrides:

    • Flags set in .env (e.g., FEATURE_NEW_DASHBOARD=true) take precedence over config file values.
    • Tip: Use php artisan config:clear to reset overrides during development.
  5. Query Builder Scope Conflicts:

    • Chaining whenFeatureIsAccessible() with other scopes may cause SQL errors if conditions are mutually exclusive.
    • Solution: Use orWhere or separate queries.
  6. Fake Scope Leaks:

    • Faked flags persist across test cases unless reset. Always call Features::fake() after tests.
    • Tip: Use Features::fake(['flag' => true])->andReturn(false) for partial mocking.

Debugging Tips

  1. Inspect Flag State:

    php artisan feature:state new-dashboard
    
    • Outputs the current value and driver source.
  2. Enable Debug Logging:

    // config/features.php
    'debug' => env('APP_DEBUG', false),
    
    • Logs flag access attempts to storage/logs/laravel.log.
  3. Check Pipeline Order:

    • Drivers are evaluated in the order defined in config/features.php. The first true result wins.
    • Tip: Place stricter drivers (e.g., DatabaseDriver) earlier in the array.

Extension Points

  1. Custom Drivers:

    • Implement YlsIdeas\FeatureFlags\Contracts\Driver:
      class SentryDriver implements Driver {
          public function accessible(string $name): bool {
              return Sentry::getUser()->hasRole('admin') && config("features.flags.{$name}");
          }
      }
      
    • Register in config/features.php:
      'drivers' => [
          \App\Drivers\SentryDriver::class,
      ],
      
  2. Event Handlers:

    • Listen for flag changes:
      Features::enable('new-feature')->listen(function ($flag) {
          Log::info("Flag {$flag->name} enabled by {$flag->enabledBy}");
      });
      
  3. Expiration Logic:

    • Add a FeatureExpired listener for time-bound flags:
      Features::expire('limited-offer')->after(function ($flag) {
          // Send notification or clean up
      });
      
  4. IDE Support:

    • Generate IDE helpers for autocompletion:
      php artisan ide-helper:generate --nowrite
      php artisan ide-helper:meta
      

Performance Considerations

  1. Avoid Redundant Checks:

    • Cache the result of Features::accessible() in a variable if called multiple times in a method:
      $canAccess = Features::accessible('flag');
      if ($canAccess) { /* ... */ }
      
  2. Lazy-Load Drivers:

    • For heavy drivers (e.g., database), defer initialization until first use by implementing Driver lazily.
  3. Batch Flag Updates:

    • Use Features::enable(['flag1', 'flag2']) to update multiple flags in a single operation.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
nexmo/api-specification
capell-app/block-library
axium/identity
cetria/laravel-dummy-models
cetria/reflection-helper
agropredict/sso-auth-bundle
evolvestudio/spam-protection
datacore/hub-sdk
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony
develia/geo-bundle
dreamzy/livewire-charts
touchestate-sdk/php-sdk
ecotone/kafka
22h/doctrine-garbage-collection-bundle
agtp/agtp-php
agtp/mod-php
splash/sonata-admin
splash/metadata