alex-bykovski/feature-manager-bundle
Installation
composer require alex-bykovski/feature-manager-bundle
Add to config/bundles.php:
AlexBykovski\FeatureManagerBundle\FeatureManagerBundle::class => ['all' => true],
Publish Configuration
php artisan vendor:publish --provider="AlexBykovski\FeatureManagerBundle\FeatureManagerBundle" --tag="config"
This creates config/feature_manager.php. Defaults:
return [
'features' => [
'feature_name' => [
'active' => true,
'default' => true,
'description' => 'Optional feature description',
],
],
];
First Use Case: Toggle a Feature In a controller or service:
use AlexBykovski\FeatureManagerBundle\FeatureManager;
public function __construct(FeatureManager $featureManager) {
$this->featureManager = $featureManager;
}
public function someAction() {
if ($this->featureManager->isActive('feature_name')) {
// Feature logic
}
}
Feature Flag Management
feature_manager:toggle Artisan command to enable/disable features without redeploying:
php artisan feature_manager:toggle feature_name --active=true
config/feature_manager.php per environment (e.g., config/feature_manager.local.php).Dependency Injection
$this->app->extend('feature_manager', function ($manager) {
$manager->addCustomRule('premium_feature', function () {
return auth()->user()->isPremium();
});
return $manager;
});
Database-Backed Features (Advanced)
features table:
// Example migration
Schema::create('features', function (Blueprint $table) {
$table->string('name')->unique();
$table->boolean('active')->default(false);
$table->timestamps();
});
FeatureManager service to query the database:
$this->app->bind('feature_manager', function () {
return new DatabaseFeatureManager(config('feature_manager.features'));
});
Grouped Features
'features' => [
'module_a' => [
'active' => true,
'sub_features' => [
'sub_feature_1' => ['active' => false],
],
],
],
$featureManager->isActive('module_a.sub_features.sub_feature_1');
Event-Based Activation
event(new UserSubscribed($user));
// In listener:
$featureManager->toggle('premium_feature', true);
Configuration Overrides
feature_manager.local.php) may not load automatically.config/feature_manager.php includes:
$features = array_merge(
require __DIR__.'/feature_manager.php',
config('feature_manager.*.php', [])
);
Caching
php artisan cache:clear
php artisan config:clear
Case Sensitivity
FeatureManager:
$featureName = strtolower($featureName);
Missing Defaults
false instead of null.if ($featureManager->has('feature_name') && $featureManager->isActive('feature_name')) {
// Safe to proceed
}
Log Feature Checks:
Add a debug method to FeatureManager:
public function debug($featureName) {
\Log::debug('Feature check for ' . $featureName, [
'active' => $this->isActive($featureName),
'config' => $this->getConfig($featureName),
]);
}
Artisan Command for Dump: Create a custom command to list all features:
php artisan feature_manager:list
Implementation:
public function handle() {
foreach (config('feature_manager.features') as $name => $config) {
$this->info(sprintf(
"%-30s | %5s | %s",
$name,
$config['active'] ? '✅' : '❌',
$config['description'] ?? ''
));
}
}
Custom Storage
FeatureManager to use Redis, DynamoDB, etc.:
class RedisFeatureManager extends FeatureManager {
public function isActive($featureName) {
return Redis::get($this->getRedisKey($featureName)) === 'true';
}
}
A/B Testing
$featureManager->isActiveForUser('experimental_feature', auth()->id());
FeatureManager:
public function isActiveForUser($featureName, $userId) {
$config = $this->getConfig($featureName);
if (!$config['active']) return false;
if (isset($config['percentage']) && $config['percentage'] < 100) {
return mt_rand(1, 100) <= $config['percentage'];
}
return true;
}
Feature Analytics
public function handle($request, Closure $next) {
if ($featureManager->isActive('analytics_enabled')) {
Analytics::track('feature_used', ['name' => 'some_feature']);
}
return $next($request);
}
Validation Rules
use Illuminate\Validation\Rule;
$rules = [
'user_input' => [
Rule::requiredIfFeatureActive('feature_name'),
],
];
FeatureManager with a static method:
public static function requiredIfFeatureActive($featureName) {
return function ($attribute, $value, $fail) {
if (!self::isActive($featureName) && empty($value)) {
$fail('The ' . $attribute . ' field is required when ' . $featureName . ' is active.');
}
};
}
How can I help you explore Laravel packages today?