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

Pennant Laravel Package

laravel/pennant

Laravel Pennant is a simple, lightweight feature flag library for Laravel. Define and evaluate feature toggles, control rollouts, and experiment safely across environments. Official docs available at laravel.com/docs/pennant.

View on GitHub
Deep Wiki
Context7
## Getting Started

### Minimal Setup
1. **Installation**:
   ```bash
   composer require laravel/pennant
   php artisan pennant:install

This publishes the migration and config file, and creates a default features table.

  1. Configuration: Review config/pennant.php to adjust:

    • Default driver (e.g., database, cache, or custom).
    • Table name (if not using the default).
    • Cache TTL for cached features.
  2. First Feature Flag: Define a feature in a migration or via the pennant:create command:

    php artisan pennant:create new-feature --value=true
    

    Or programmatically:

    use Laravel\Pennant\Feature;
    
    Feature::create('new-feature', true);
    
  3. Usage in Code: Check a feature flag:

    if (Feature::get('new-feature')) {
        // Feature is active
    }
    

    Or use the helper:

    if (pennant('new-feature')) {
        // Feature is active
    }
    

Implementation Patterns

Core Workflows

  1. Feature Flag Management:

    • Creation: Use Feature::create('name', $value, $scope = null) or the pennant:create Artisan command.
    • Updating: Use Feature::update('name', $value) or bulk updates with Feature::updateMany([...]).
    • Deletion: Use Feature::delete('name') or Feature::purge() for all features.
  2. Scoped Features: Define features for specific scopes (e.g., user roles, environments):

    Feature::create('beta-feature', true, 'admin');
    

    Retrieve scoped features:

    Feature::get('beta-feature', 'admin'); // Returns true
    Feature::get('beta-feature'); // Returns null (no default scope)
    
  3. Middleware Integration: Protect routes with feature flags:

    Route::middleware(['feature:new-feature'])->group(function () {
        // Routes only accessible if 'new-feature' is active
    });
    

    Or use the EnsureFeaturesAreActive middleware:

    Route::middleware(['feature:new-feature|beta-feature'])->group(...);
    
  4. Blade Directives: Toggle UI elements dynamically:

    @feature('new-feature')
        <button>Enable New Feature</button>
    @endfeature
    

    Or use @featureany for multiple flags:

    @featureany('flag1|flag2')
        <div>At least one feature is active</div>
    @endfeatureany
    
  5. Events and Hooks: Listen for feature updates:

    use Laravel\Pennant\Events\FeatureUpdated;
    
    FeatureUpdated::listen(function ($event) {
        Log::info("Feature {$event->feature->name} updated to {$event->value}");
    });
    

    Use before hooks for pre-flight checks:

    Feature::create('premium-feature', false, 'premium')
        ->before(function ($feature) {
            return auth()->user()->isPremium();
        });
    
  6. Bulk Operations: Load all features at once (e.g., for caching):

    $allFeatures = Feature::loadAll();
    

    Update multiple features efficiently:

    Feature::updateMany([
        'feature1' => true,
        'feature2' => false,
    ]);
    

Integration Tips

  1. Custom Drivers: Extend the Driver interface to support non-database backends (e.g., Redis, DynamoDB):

    use Laravel\Pennant\Contracts\Driver;
    
    class RedisDriver implements Driver {
        public function get($name, $scope = null) { ... }
        public function set($name, $value, $scope = null) { ... }
        // ... other methods
    }
    

    Register in config/pennant.php:

    'driver' => \App\Drivers\RedisDriver::class,
    
  2. Testing: Use the Feature facade in tests to mock feature flags:

    public function test_feature_flag()
    {
        Feature::shouldReceive('get')
                ->with('test-feature')
                ->andReturn(true);
    
        $this->assertTrue(pennant('test-feature'));
    }
    

    Or use the FeatureTestCase trait for cleaner assertions:

    use Laravel\Pennant\Testing\FeatureTestCase;
    
    class MyTest extends FeatureTestCase {
        public function test_feature()
        {
            $this->assertFeatureActive('test-feature');
        }
    }
    
  3. Performance:

    • Cache feature flags in memory (e.g., Redis) for high-traffic apps.
    • Use Feature::loadAll() to preload flags if checking multiple in a single request.
    • Disable caching for development ('cache_ttl' => 0 in config).
  4. Database Optimization:

    • For high-write workloads, use the insertAll method (v1.8.1+) to batch inserts.
    • Customize the updated_at column name in migrations if needed.

Gotchas and Tips

Common Pitfalls

  1. Scope Mismatches:

    • Issue: Forgetting to specify a scope when retrieving a scoped feature.
      Feature::get('admin-feature'); // Returns null if no default scope is set
      
    • Fix: Always pass the scope or set a default in config/pennant.php:
      'default_scope' => 'user',
      
  2. Cache Staleness:

    • Issue: Features not updating due to cached values.
    • Fix: Manually flush the cache:
      Feature::flushCache();
      
      Or configure automatic cache flushing in config/pennant.php:
      'cache_flush_on_update' => true,
      
  3. Middleware Misconfiguration:

    • Issue: EnsureFeaturesAreActive middleware failing silently.
    • Fix: Ensure the middleware is properly registered in app/Http/Kernel.php:
      protected $routeMiddleware = [
          'feature' => \Laravel\Pennant\Http\Middleware\EnsureFeaturesAreActive::class,
      ];
      
    • For Laravel 11+, use the feature alias:
      Route::middleware(['feature:flag1,flag2'])->group(...);
      
  4. Type Hints and Scopes:

    • Issue: Primitive types (e.g., bool, int) failing scope validation.
    • Fix: Update to v1.18.3+ or explicitly cast types:
      Feature::create('numeric-feature', (int)100, 'scope');
      
  5. Migration Conflicts:

    • Issue: Table name conflicts if pennant:install is run multiple times.
    • Fix: Manually adjust the migration file or use --force:
      php artisan migrate --force
      
  6. Blade Directive Scope:

    • Issue: @feature directives not working as expected.
    • Fix: Ensure the directive is registered in AppServiceProvider@boot():
      if (! Blade::directive('feature')) {
          Blade::directive('feature', function ($expression) {
              return "<?php if (\\Laravel\\Pennant\\Feature::get({$expression})): ?>";
          });
      }
      

Debugging Tips

  1. Log Feature Checks: Add a temporary log to debug feature retrieval:

    \Log::debug('Feature check', [
        'name' => 'test-feature',
        'scope' => 'user',
        'value' => Feature::get('test-feature', 'user'),
    ]);
    
  2. Check Database Values: Verify feature values in the database:

    php artisan tinker
    >>> \DB::table('features')->where('name', 'test-feature')->get();
    
  3. Validate Config: Ensure config/pennant.php matches your environment:

    return [
        'driver' => env('PENNANT_DRIVER', 'database'),
        'table' => env('PENNANT_TABLE', 'features'),
        'default_scope' => null,
        'cache_ttl' => env('PENNANT_CACHE_TTL', 60),
    ];
    

Extension Points

  1. Custom Feature Classes: Extend the Feature class to add domain-specific logic:

    class CustomFeature extends \Laravel\Pennant\Feature {
        public static function isPremium($name) {
            return self::get($name, 'premium') ?? false;
        }
    }
    
  2. Driver Decorators: Wrap the

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.
calmfox/watch-sylius
damienfern/grpc-symfony-bundle
atoolo/index-bundle
atoolo/genai-bundle
coprotoai/laravel-ticket
davidjln/llm-carbon-bundle
cryonighter/valid-request-bundle
coolms/taxonomy-bundle
coolms/field-bundle
articulate-orm/symfony
aaix/laravel-tall-architect
ephoto/akeneo-connector
emmanuelballery/eb-plantumlbundle
emielburgman/symfony-visitor-beacon
emielburgman/symfony-visit-storage
emielburgman/symfony-security-headers
emielburgman/symfony-log-viewer
emarref/xdebug-bundle
emarref/pubnub-bundle
elriseio/finance-money-bundle