diff --git a/src/Commands/DisableFeatureCommand.php b/src/Commands/DisableFeatureCommand.php new file mode 100644 index 0000000..04ca944 --- /dev/null +++ b/src/Commands/DisableFeatureCommand.php @@ -0,0 +1,47 @@ +argument('name'); + + if (Feature::disable($featureName) !== null) { + + $this->info("Turned feature {$featureName} off"); + return; + } + + $this->info("Feature {$featureName} already off"); + + } catch (FeatureNotFoundException $exception) { + $this->warn("Feature {$exception->featureName} could not be found"); + } + } +} \ No newline at end of file diff --git a/src/Commands/EnableFeatureCommand.php b/src/Commands/EnableFeatureCommand.php new file mode 100644 index 0000000..dd59f10 --- /dev/null +++ b/src/Commands/EnableFeatureCommand.php @@ -0,0 +1,47 @@ +argument('name'); + + if (Feature::enable($featureName) !== null) { + + $this->info("Turned feature {$featureName} on"); + return; + } + + $this->info("Feature {$featureName} already on"); + + } catch (FeatureNotFoundException $exception) { + $this->warn("Feature {$exception->featureName} could not be found"); + } + } +} \ No newline at end of file diff --git a/src/Commands/InsertNewFeaturesCommand.php b/src/Commands/InsertNewFeaturesCommand.php index 5d7b350..d98ad1f 100644 --- a/src/Commands/InsertNewFeaturesCommand.php +++ b/src/Commands/InsertNewFeaturesCommand.php @@ -13,25 +13,25 @@ class InsertNewFeaturesCommand extends Command * * @var string */ - protected $signature = 'features:insert'; + protected $signature = 'feature:create'; /** * The console command description. * * @var string */ - protected $description= 'Reads features from config, and persists them'; + protected $description= 'Reads features from config, and inserts them in the database'; /** * Execute the console command. * * @return void */ - protected function handle() + public function handle() { collect(Config::get('features.features', null)) - ->each(function (string $feature) { - if (Feature::createFeature($feature) !== null) { + ->each(function (int $default, string $feature ) { + if (Feature::createFeature($feature, $default) !== null) { $this->info("Feature created: {$feature}."); } else { $this->info("Feature already exists: {$feature}."); diff --git a/src/Commands/ListFeaturesCommand.php b/src/Commands/ListFeaturesCommand.php new file mode 100644 index 0000000..28aee4e --- /dev/null +++ b/src/Commands/ListFeaturesCommand.php @@ -0,0 +1,33 @@ +table(['id', 'name', 'state', 'created_at', 'updated_at'], Feature::allArray()); + } +} \ No newline at end of file diff --git a/src/Config/features.php b/src/Config/features.php index 066c847..26867c8 100644 --- a/src/Config/features.php +++ b/src/Config/features.php @@ -2,7 +2,6 @@ use MatthiasWilbrink\FeatureToggle\Models\Feature; - return [ /* @@ -10,13 +9,24 @@ | Available features |-------------------------------------------------------------------------- | - | + | All features must be registered here, + | together with their default state. | */ 'features' => [ - 'demo_feature'=> Feature::OFF, + 'demo_feature' => Feature::OFF, ], - 'default_behaviour'=> Feature::OFF, + /* + |-------------------------------------------------------------------------- + | Default behaviour + |-------------------------------------------------------------------------- + | + | This defines the state a feature will assume + | when it is not defined in the array above. + | + */ + + 'default_behaviour' => Feature::OFF, ]; \ No newline at end of file diff --git a/src/Exceptions/FeatureNotFoundException.php b/src/Exceptions/FeatureNotFoundException.php new file mode 100644 index 0000000..b66b805 --- /dev/null +++ b/src/Exceptions/FeatureNotFoundException.php @@ -0,0 +1,24 @@ +featureName = $featureName; + + parent::__construct("", 0, null); + } +} \ No newline at end of file diff --git a/src/Helpers.php b/src/Helpers.php index a7bf063..6c61204 100644 --- a/src/Helpers.php +++ b/src/Helpers.php @@ -1,7 +1,10 @@ isOn($name); + return (app(FeatureManager::class))->isEnabled($name); } } \ No newline at end of file diff --git a/src/Managers/FeatureManager.php b/src/Managers/FeatureManager.php index 9ac7b71..687ee6e 100644 --- a/src/Managers/FeatureManager.php +++ b/src/Managers/FeatureManager.php @@ -3,42 +3,100 @@ namespace MatthiasWilbrink\FeatureToggle\Managers; use Illuminate\Support\Facades\Config; +use MatthiasWilbrink\FeatureToggle\Exceptions\FeatureNotFoundException; use MatthiasWilbrink\FeatureToggle\Models\Feature; +/** + * Class FeatureManager + * @package MatthiasWilbrink\FeatureToggle\Managers + */ class FeatureManager { - public function isOn(string $name) + /** + * Check if a feature is enabled + * + * @param string $name + * @return bool + */ + public function isEnabled(string $name): bool { $feature = $this->getFeature($name); return $feature ? $feature->state : $this->default(); } - public function default() + /** + * Check if a feature is enabled + * + * isOn is an alias of isEnabled + * + * @param string $name + * @return bool + */ + public function isOn(string $name): bool + { + return $this->isEnabled($name); + } + + /** + * Return the default state + * + * @return bool + */ + public function default(): bool { return Config::get('features.default_behaviour', Feature::OFF); } - public function turnOn(string $name) + /** + * Enable a feature + * + * @param string $name + * @return Feature|null + * @throws FeatureNotFoundException + */ + public function enable(string $name) { $feature = $this->getFeature($name); if ($feature === null) { - return; + throw new FeatureNotFoundException($name); + } + if ($feature->state === Feature::ON) { + return null; } $feature->state = Feature::ON; $feature->save(); + return $feature; } - public function turnOff(string $name) + /** + * Disable a feature + * + * @param string $name + * @return Feature|null + * @throws FeatureNotFoundException + */ + public function disable(string $name) { $feature = $this->getFeature($name); if ($feature === null) { - return; + throw new FeatureNotFoundException($name); + } + if ($feature->state === Feature::OFF) { + return null; } $feature->state = Feature::OFF; $feature->save(); + return $feature; } + /** + * Create a feature + * + * @param string $name + * @param int $initialState + * @return Feature|null + */ public function createFeature(string $name, int $initialState) { $feature = $this->getFeature($name); @@ -52,6 +110,35 @@ public function createFeature(string $name, int $initialState) return $feature; } + /** + * Get all features as collection + * + * @return \Illuminate\Database\Eloquent\Collection|Feature[] + */ + public function all() + { + return Feature::all(); + } + + /** + * Get all features as array + * + * @return array + */ + public function allArray() + { + return $this->all()->map(function ($feature) { + $feature->state = $feature->stateLabel; + return $feature; + })->toArray(); + } + + /** + * Get feature by name if it exists + * + * @param string $name + * @return Feature|null + */ private function getFeature(string $name) { return Feature::where('name', $name)->first(); diff --git a/src/Models/Feature.php b/src/Models/Feature.php index 9540978..9b8cf0d 100644 --- a/src/Models/Feature.php +++ b/src/Models/Feature.php @@ -28,4 +28,10 @@ class Feature extends Model * @var string */ protected $table = 'feature_toggles'; + + + public function getStateLabelAttribute() + { + return $this->state ? 'On' : 'Off'; + } } \ No newline at end of file diff --git a/src/Providers/FeatureToggleServiceProvider.php b/src/Providers/FeatureToggleServiceProvider.php index d00e9cd..c529c20 100644 --- a/src/Providers/FeatureToggleServiceProvider.php +++ b/src/Providers/FeatureToggleServiceProvider.php @@ -4,41 +4,64 @@ use Illuminate\Support\Facades\Blade; use Illuminate\Support\ServiceProvider; +use MatthiasWilbrink\FeatureToggle\Commands\DisableFeatureCommand; +use MatthiasWilbrink\FeatureToggle\Commands\EnableFeatureCommand; use MatthiasWilbrink\FeatureToggle\Commands\InsertNewFeaturesCommand; +use MatthiasWilbrink\FeatureToggle\Commands\ListFeaturesCommand; use MatthiasWilbrink\FeatureToggle\Facades\Feature; class FeatureToggleServiceProvider extends ServiceProvider { + /** + * Boot the Feature Service Provider + */ public function boot() { - - $this->loadMigrationsFrom(__DIR__.'/../Migrations'); - $this->publishes([ - __DIR__.'/../Config/features.php' => config_path('features.php'), - ], 'config'); - + $this->bootMigrations(); + $this->bootConfig(); $this->bootBlade(); - $this->bootCommands(); } - public function register() + /** + * Add migrations + */ + private function bootMigrations() { + $this->loadMigrationsFrom(__DIR__ . '/../Migrations'); + } + /** + * Add config + */ + private function bootConfig() + { + $this->publishes([ + __DIR__ . '/../Config/features.php' => config_path('features.php'), + ], 'config'); } + /** + * Add blade directive + */ private function bootBlade() { Blade::if('feature', function ($name) { - return Feature::isOn($name); + return Feature::isEnabled($name); }); } + /** + * Add commands + */ private function bootCommands() { if ($this->app->runningInConsole()) { $this->commands([ InsertNewFeaturesCommand::class, + ListFeaturesCommand::class, + EnableFeatureCommand::class, + DisableFeatureCommand::class, ]); } }