├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── composer.json └── src ├── Concerns └── DefinesFeatures.php ├── Exceptions └── FeatureException.php └── ServiceProvider.php /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to `enum-features` will be documented in this file. 4 | 5 | ## v2.0.5 - 2024-04-26 6 | 7 | **Full Changelog**: https://github.com/defstudio/enum-features/compare/v2.0.4...v2.0.5 8 | 9 | ## v2.0.4 - 2024-04-26 10 | 11 | **Full Changelog**: https://github.com/defstudio/enum-features/compare/v2.0.3...v2.0.4 12 | 13 | ## v2.0.3 - 2024-04-19 14 | 15 | **Full Changelog**: https://github.com/defstudio/enum-features/compare/v2.0.2...v2.0.3 16 | 17 | ## v2.0.2 - 2024-04-19 18 | 19 | **Full Changelog**: https://github.com/defstudio/enum-features/compare/v2.0.1...v2.0.2 20 | 21 | ## v2.0.1 - 2024-04-19 22 | 23 | **Full Changelog**: https://github.com/defstudio/enum-features/compare/v2.0.0...v2.0.1 24 | 25 | ## v2.0.0 - 2024-04-19 26 | 27 | ### What's Changed 28 | 29 | * Bump actions/checkout from 3 to 4 by @dependabot in https://github.com/defstudio/enum-features/pull/3 30 | * Bump stefanzweifel/git-auto-commit-action from 4 to 5 by @dependabot in https://github.com/defstudio/enum-features/pull/4 31 | * Bump aglipanci/laravel-pint-action from 2.3.0 to 2.3.1 by @dependabot in https://github.com/defstudio/enum-features/pull/5 32 | * Bump dependabot/fetch-metadata from 1.6.0 to 2.0.0 by @dependabot in https://github.com/defstudio/enum-features/pull/7 33 | * Bump aglipanci/laravel-pint-action from 2.3.1 to 2.4 by @dependabot in https://github.com/defstudio/enum-features/pull/8 34 | 35 | **Full Changelog**: https://github.com/defstudio/enum-features/compare/v1.1.0...v2.0.0 36 | 37 | ## v1.1.0 - 2023-07-05 38 | 39 | **Full Changelog**: https://github.com/defstudio/enum-features/compare/v1.0.0...v1.1.0 40 | 41 | ## v1.0.0 - 2023-07-03 42 | 43 | ### What's Changed 44 | 45 | - Starting release 46 | 47 | **Full Changelog**: https://github.com/defstudio/enum-features/commits/v1.0.0 48 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) defstudio 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Enum Features 2 | 3 | [![Latest Version on Packagist](https://img.shields.io/packagist/v/defstudio/enum-features.svg?style=flat-square)](https://packagist.org/packages/defstudio/enum-features) 4 | [![GitHub Tests Action Status](https://img.shields.io/github/actions/workflow/status/defstudio/enum-features/run-tests.yml?branch=main&label=tests&style=flat-square)](https://github.com/defstudio/enum-features/actions?query=workflow%3Arun-tests+branch%3Amain) 5 | [![GitHub Code Style Action Status](https://img.shields.io/github/actions/workflow/status/defstudio/enum-features/fix-php-code-style-issues.yml?branch=main&label=code%20style&style=flat-square)](https://github.com/defstudio/enum-features/actions?query=workflow%3A"Fix+PHP+code+style+issues"+branch%3Amain) 6 | [![Total Downloads](https://img.shields.io/packagist/dt/defstudio/enum-features.svg?style=flat-square)](https://packagist.org/packages/defstudio/enum-features) 7 | [![License](https://img.shields.io/packagist/l/defstudio/telegraph?style=flat&cacheSeconds=3600)](https://packagist.org/packages/defstudio/enum-features) 8 | [![Twitter Follow](https://img.shields.io/twitter/follow/FabioIvona?label=Follow&style=social)](https://twitter.com/FabioIvona?ref_src=twsrc%5Etfw) 9 | 10 | A simple trait to enable a feature system using Enums: 11 | 12 | ```php 13 | if(AppFeature::welcome_email->active()){ 14 | Mail::to($newUser)->send(new WelcomeEmail($newUser)); 15 | } 16 | ``` 17 | 18 | ## Installation 19 | 20 | You can install the package via composer: 21 | 22 | ```bash 23 | composer require defstudio/enum-features 24 | ``` 25 | 26 | ## Usage 27 | 28 | Features can be enabled on any enum by using the `DefinesFeatures` trait: 29 | 30 | ```php 31 | use DefStudio\EnumFeatures\EnumFeatures; 32 | 33 | enum AppFeature 34 | { 35 | use DefinesFeatures; // ← simply add this 36 | 37 | case multi_language; 38 | case impersonate; 39 | case welcome_email; 40 | 41 | /* Feature resolution */ 42 | 43 | //with a single method: 44 | protected function resolve(Authenticatable $user = null) { 45 | match($this){ 46 | case self::multi_language => true, 47 | case self::impersonate => $user->isAdmin(), 48 | default => false; 49 | } 50 | } 51 | 52 | //or with a dedicated method: 53 | 54 | protected function resolveImpersonate(Authenticatable $user = null){ 55 | return $user->isSuperAdmin(); 56 | } 57 | } 58 | ``` 59 | 60 | and should be registered in your Provider 61 | 62 | ```php 63 | class AppServiceProvider extends ServiceProvider 64 | { 65 | //.. 66 | 67 | public function boot(): void { 68 | AppFeature::defineFeatures(); 69 | } 70 | } 71 | ``` 72 | 73 | 74 | then, in code, a feature could be checked to be enabled: 75 | 76 | ```php 77 | if(AppFeature::multi_language->active()){ 78 | //.. multi language specific code 79 | } 80 | ``` 81 | 82 | An extensive documentation is available at 83 | 84 | https://docs.defstudio.it/enum-features 85 | 86 | ## Testing 87 | 88 | ```bash 89 | composer test 90 | ``` 91 | 92 | ## Changelog 93 | 94 | Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently. [Follow Us](https://twitter.com/FabioIvona) on Twitter for more updates about this package. 95 | 96 | ## Contributing 97 | 98 | Please see [CONTRIBUTING](.github/CONTRIBUTING.md) for details. 99 | 100 | ## Security Vulnerabilities 101 | 102 | Please review [our security policy](../../security/policy) on how to report security vulnerabilities. 103 | 104 | ## Credits 105 | 106 | - [Fabio Ivona](https://github.com/defstudio) 107 | - [def:studio team](https://github.com/defstudio) 108 | - [All Contributors](../../contributors) 109 | 110 | ## License 111 | 112 | The MIT License (MIT). Please see [License File](LICENSE.md) for more information. 113 | 114 | ## Support us 115 | 116 | We at [def:studio](https://github.com/defstudio) strongly believe that open source is the foundation of all our business and we try to contribute to it by helping other projects to grow along with developing and maintaining our packages. You can support our work by sponsoring us on [github](https://github.com/sponsors/defstudio)! 117 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "defstudio/enum-features", 3 | "description": "A simple trait to enable a feature system using Enums and Laravel Pennant", 4 | "keywords": [ 5 | "defstudio", 6 | "laravel", 7 | "pennant", 8 | "enum-features", 9 | "enums", 10 | "features" 11 | ], 12 | "homepage": "https://github.com/defstudio/enum-features", 13 | "license": "MIT", 14 | "authors": [ 15 | { 16 | "name": "Fabio Ivona", 17 | "email": "fabio.ivona@defstudio.it", 18 | "role": "Developer" 19 | } 20 | ], 21 | "require": { 22 | "php": "^8.3", 23 | "spatie/laravel-package-tools": "^1.16.4", 24 | "illuminate/support": "^v11.4.0", 25 | "laravel/pennant": "^v1.7.0" 26 | }, 27 | "require-dev": { 28 | "larastan/larastan": "^v2.9.5", 29 | "laravel/pint": "^v1.15.1", 30 | "nunomaduro/collision": "^v8.1.1", 31 | "orchestra/testbench": "^v9.0.4", 32 | "pestphp/pest": "^v2.34.7", 33 | "pestphp/pest-plugin-laravel": "^v2.3.0", 34 | "phpstan/extension-installer": "^1.3.1", 35 | "phpstan/phpstan-deprecation-rules": "^1.1.4", 36 | "phpstan/phpstan-phpunit": "^1.3.16", 37 | "spatie/invade": "^2.0" 38 | }, 39 | "autoload": { 40 | "psr-4": { 41 | "DefStudio\\EnumFeatures\\": "src/" 42 | } 43 | }, 44 | "autoload-dev": { 45 | "psr-4": { 46 | "DefStudio\\EnumFeatures\\Tests\\": "tests/" 47 | } 48 | }, 49 | "scripts": { 50 | "post-autoload-dump": "@php ./vendor/bin/testbench package:discover --ansi", 51 | "analyse": "vendor/bin/phpstan analyse", 52 | "test": "vendor/bin/pest", 53 | "test-coverage": "vendor/bin/pest --coverage", 54 | "format": "vendor/bin/pint" 55 | }, 56 | "config": { 57 | "sort-packages": true, 58 | "allow-plugins": { 59 | "pestphp/pest-plugin": true, 60 | "phpstan/extension-installer": true 61 | } 62 | }, 63 | "extra": { 64 | "laravel": { 65 | "providers": [ 66 | "DefStudio\\EnumFeatures\\ServiceProvider" 67 | ] 68 | } 69 | }, 70 | "minimum-stability": "dev", 71 | "prefer-stable": true 72 | } 73 | -------------------------------------------------------------------------------- /src/Concerns/DefinesFeatures.php: -------------------------------------------------------------------------------- 1 | featureName(), $this->resolve(...)); 19 | } 20 | 21 | protected function featureName(): string 22 | { 23 | return $this instanceof BackedEnum ? $this->value : $this->name; 24 | } 25 | 26 | public function active(?Authenticatable $scope = null): bool 27 | { 28 | if ($scope) { 29 | return Pennant::for($scope)->active($this->featureName()); 30 | } 31 | 32 | return Pennant::active($this->featureName()); 33 | } 34 | 35 | public function inactive(?Authenticatable $scope = null): bool 36 | { 37 | return ! $this->active($scope); 38 | } 39 | 40 | public function middleware(): string 41 | { 42 | return EnsureFeaturesAreActive::using($this->featureName()); 43 | } 44 | 45 | protected function resolve(?Authenticatable $scope = null): bool 46 | { 47 | $featureName = $this->featureName(); 48 | $camelFeatureName = str($this->featureName())->camel()->ucfirst(); 49 | 50 | $try_methods = [ 51 | "resolve_$featureName", 52 | "resolve_{$featureName}_feature", 53 | "resolve$camelFeatureName", 54 | "resolve{$camelFeatureName}Feature", 55 | ]; 56 | 57 | foreach ($try_methods as $method) { 58 | if (method_exists($this, $method)) { 59 | return $this->{$method}($scope); 60 | } 61 | } 62 | 63 | return false; 64 | } 65 | 66 | public function enforce(?Authenticatable $scope = null): void 67 | { 68 | if (! $this->active($scope)) { 69 | abort(403); 70 | } 71 | } 72 | 73 | public function activate(?Authenticatable $scope = null): void 74 | { 75 | if ($scope) { 76 | Pennant::for($scope)->activate($this->featureName()); 77 | 78 | return; 79 | } 80 | 81 | Pennant::activate($this->featureName()); 82 | } 83 | 84 | public function deactivate(?Authenticatable $scope = null): void 85 | { 86 | if ($scope) { 87 | Pennant::for($scope)->deactivate($this->featureName()); 88 | 89 | return; 90 | } 91 | 92 | Pennant::deactivate($this->featureName()); 93 | } 94 | 95 | public function forget(?Authenticatable $scope = null): void 96 | { 97 | if ($scope) { 98 | Pennant::for($scope)->forget($this->featureName()); 99 | 100 | return; 101 | } 102 | 103 | Pennant::forget($this->featureName()); 104 | } 105 | 106 | public function purge(): void 107 | { 108 | Pennant::purge($this->featureName()); 109 | } 110 | 111 | public static function defineFeatures(): void 112 | { 113 | collect(self::cases())->each->define(); 114 | } 115 | 116 | /** 117 | * @param array $features 118 | */ 119 | public static function areAllActive(array $features): bool 120 | { 121 | return Pennant::allAreActive(collect($features) 122 | ->map(fn (self $feature) => $feature->featureName()) 123 | ->toArray()); 124 | } 125 | 126 | /** 127 | * @param array $features 128 | */ 129 | public static function someAreActive(array $features): bool 130 | { 131 | return Pennant::someAreActive(collect($features) 132 | ->map(fn (self $feature) => $feature->featureName()) 133 | ->toArray()); 134 | } 135 | 136 | /** 137 | * @param array $features 138 | */ 139 | public static function areAllInactive(array $features): bool 140 | { 141 | return Pennant::allAreInactive(collect($features) 142 | ->map(fn (self $feature) => $feature->featureName()) 143 | ->toArray()); 144 | } 145 | 146 | /** 147 | * @param array $features 148 | */ 149 | public static function someAreInactive(array $features): bool 150 | { 151 | return Pennant::someAreInactive(collect($features) 152 | ->map(fn (self $feature) => $feature->featureName()) 153 | ->toArray()); 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /src/Exceptions/FeatureException.php: -------------------------------------------------------------------------------- 1 | value : $feature->name; 14 | 15 | return new self("Enum [$name] does not use DefineFeatures trait"); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/ServiceProvider.php: -------------------------------------------------------------------------------- 1 | name('enum-features'); 17 | } 18 | 19 | public function packageBooted(): void 20 | { 21 | Blade::if('feature', function (UnitEnum $feature) { 22 | if (! class_uses($feature, DefinesFeatures::class)) { 23 | throw FeatureException::invalid_feature($feature); 24 | } 25 | 26 | /** @phpstan-ignore-next-line */ 27 | return $feature->active(); 28 | }); 29 | } 30 | } 31 | --------------------------------------------------------------------------------