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 Bundle Laravel Package

adespresso/feature-bundle

Symfony bundle for feature releases and rollouts. Manage feature flags and enable new functionality for specific subsets of users. Includes API docs, documentation in Resources/doc, tests, and Apache 2.0 license.

View on GitHub
Deep Wiki
Context7
## Getting Started

### Minimal Setup
1. **Installation**:
   ```bash
   composer require adespresso/feature-bundle

Register the bundle in config/bundles.php (Symfony) or config/app.php (Laravel via Symfony bridge):

return [
    // ...
    Adespresso\FeatureBundle\AdespressoFeatureBundle::class => ['all' => true],
];
  1. Configuration: Publish the default config:

    php artisan vendor:publish --provider="Adespresso\FeatureBundle\AdespressoFeatureBundle" --tag="config"
    

    Edit config/feature_bundle.php to define your feature flags (e.g., new_dashboard, beta_payment).

  2. First Use Case: Enable a feature for a user group (e.g., premium_users) via CLI:

    php bin/console feature:enable new_dashboard premium_users
    

    Check if a feature is enabled in a controller:

    use Adespresso\FeatureBundle\FeatureChecker;
    
    public function index(FeatureChecker $featureChecker)
    {
        if ($featureChecker->isEnabled('new_dashboard')) {
            return view('new_dashboard');
        }
        return view('old_dashboard');
    }
    
  3. User Groups: Define groups in config/feature_bundle.php under groups:

    'groups' => [
        'premium_users' => ['user_id' => 1, 'user_id' => 2], // Hardcoded (for testing)
        // OR use a service to dynamically fetch users (e.g., from DB)
    ],
    

Implementation Patterns

Core Workflows

  1. Feature Flag Management:

    • Enable/Disable Features:
      php bin/console feature:enable new_dashboard premium_users
      php bin/console feature:disable new_dashboard
      
    • Toggle for All Users:
      php bin/console feature:toggle new_dashboard --all
      
  2. Dynamic User Grouping:

    • Use a custom service to fetch groups dynamically (e.g., from a database):
      // src/Service/UserGroupProvider.php
      class UserGroupProvider implements UserGroupProviderInterface
      {
          public function getGroupUsers(string $groupName): array
          {
              return User::where('role', $groupName)->pluck('id')->toArray();
          }
      }
      
    • Register the service in config/feature_bundle.php:
      'user_group_provider' => App\Service\UserGroupProvider::class,
      
  3. Integration with Laravel:

    • Service Provider: Bind the FeatureChecker to Laravel’s container in AppServiceProvider:
      public function register()
      {
          $this->app->bind(FeatureChecker::class, function ($app) {
              return new FeatureChecker(
                  $app['feature.manager'],
                  $app['feature.storage'],
                  $app['feature.user_group_provider']
              );
          });
      }
      
    • Middleware for Feature Gating:
      // app/Http/Middleware/FeatureGate.php
      public function handle($request, Closure $next)
      {
          if (!$this->featureChecker->isEnabled('required_feature')) {
              abort(403, 'Feature not available for your account.');
          }
          return $next($request);
      }
      
  4. Environment-Specific Flags:

    • Use the environment key in config/feature_bundle.php to enable features per environment:
      'features' => [
          'new_dashboard' => [
              'enabled' => env('FEATURE_NEW_DASHBOARD', false),
              'groups' => ['premium_users'],
          ],
      ],
      
  5. Database Backend:

    • Switch to a database-backed storage (requires doctrine/doctrine-bundle):
      'storage' => [
          'type' => 'database',
          'connection' => 'default',
      ],
      
    • Run migrations:
      php bin/console doctrine:migrations:diff
      php bin/console doctrine:migrations:migrate
      

Gotchas and Tips

Pitfalls

  1. User Group Staleness:

    • Hardcoded groups in config won’t update dynamically. Always use a service (e.g., UserGroupProvider) for production.
    • Fix: Implement a cron job or event listener to refresh groups periodically.
  2. Caching Issues:

    • Feature flags may be cached aggressively. Clear cache after changes:
      php artisan cache:clear
      php artisan config:clear
      
    • Tip: Use feature:clear-cache command if provided.
  3. Laravel-Symfony Bridge Quirks:

    • The bundle is Symfony-first. For Laravel, ensure:
      • The FeatureChecker is properly bound to the container.
      • Console commands are registered (check AppServiceProvider).
    • Fix: Extend the bundle’s FeatureBundle to add Laravel-specific logic.
  4. Permission Overrides:

    • Features enabled via --all can’t be overridden by user groups. Use --except carefully:
      php bin/console feature:enable new_dashboard --all --except beta_testers
      
  5. Database Schema Mismatch:

    • If using the database backend, ensure the feature and feature_group tables exist. Run migrations if missing.

Debugging Tips

  1. Check Feature Status:

    • Use the feature:list command to debug enabled features:
      php bin/console feature:list
      
    • For a specific user, inject FeatureChecker and log results:
      $featureChecker->isEnabled('feature_name', ['user_id' => 123]);
      
  2. Log User Group Resolution:

    • Extend UserGroupProvider to log which users are in which groups:
      public function getGroupUsers(string $groupName): array
      {
          $users = User::where('role', $groupName)->pluck('id')->toArray();
          \Log::debug("Group {$groupName} resolved to users: " . json_encode($users));
          return $users;
      }
      
  3. Environment-Specific Debugging:

    • Add debug logs to config/feature_bundle.php:
      'debug' => env('APP_DEBUG', false),
      
    • Enable via .env:
      APP_DEBUG=true
      

Extension Points

  1. Custom Storage Backends:

    • Implement Adespresso\FeatureBundle\Storage\StorageInterface for Redis, Memcached, etc.:
      class RedisStorage implements StorageInterface
      {
          public function saveFeature(Feature $feature) { /* ... */ }
          public function getFeature(string $name) { /* ... */ }
      }
      
    • Register in config:
      'storage' => [
          'type' => 'custom',
          'class' => App\Storage\RedisStorage::class,
      ],
      
  2. Event Listeners:

    • Listen for feature enable/disable events:
      // src/EventListener/FeatureListener.php
      public static function getSubscribedEvents()
      {
          return [
              FeatureEvents::FEATURE_ENABLED => 'onFeatureEnabled',
          ];
      }
      
      public function onFeatureEnabled(FeatureEnabledEvent $event)
      {
          \Log::info("Feature {$event->getFeatureName()} enabled for groups: " . implode(', ', $event->getGroups()));
      }
      
  3. Custom Feature Logic:

    • Extend FeatureChecker to add conditions (e.g., time-based, geo-based):
      class CustomFeatureChecker extends FeatureChecker
      {
          public function isEnabled(string $name, array $context = []): bool
          {
              if ($name === 'time_limited_feature' && now()->hour < 9) {
                  return false;
              }
              return parent::isEnabled($name, $context);
          }
      }
      
    • Bind the custom checker in AppServiceProvider.
  4. API for Frontend:

    • Expose feature flags via an API endpoint:
      Route::get('/api/features', function (FeatureChecker $featureChecker, Request $request) {
          return [
              'new_dashboard' => $featureChecker->isEnabled('new_dashboard', ['user_id' => $request->user()->id]),
          ];
      });
      
    • Cache responses aggressively for performance.

Performance Tips

  1. Avoid Runtime Group Resolution:

    • Pre-fetch user groups in a scheduled job if they change infrequently:
      // app/Console/Commands/RefreshUserGroups.php
      public function handle()
      {
          $provider = $this->container->get(UserGroupProvider::class);
          foreach (config('feature_bundle.groups') as $group => $users) {
              $provider->refreshGroup($group);
          }
      }
      
  2. Use Context Sparingly:

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.
comsave/common
alecsammon/php-raml-parser
chrome-php/wrench
lendable/composer-license-checker
typhoon/reflection
mesilov/moneyphp-percentage
mike42/gfx-php
bookdown/themes
aura/view
aura/html
aura/cli
povils/phpmnd
nayjest/manipulator
omnipay/tests
psr-mock/http-message-implementation
psr-mock/http-factory-implementation
psr-mock/http-client-implementation
voku/email-check
voku/urlify
rtheunissen/guzzle-log-middleware