|
1 | | - |
2 | | -[<img src="https://github-ads.s3.eu-central-1.amazonaws.com/support-ukraine.svg?t=1" />](https://supportukrainenow.org) |
3 | | - |
4 | 1 | # Dynamic feature flags for laravel. |
5 | 2 |
|
6 | 3 | [](https://packagist.org/packages/codinglabsau/laravel-feature-flags) |
7 | | -[](https://github.com/codinglabsau/laravel-feature-flags/actions?query=workflow%3Arun-tests+branch%3Amain) |
8 | | -[](https://github.com/codinglabsau/laravel-feature-flags/actions?query=workflow%3A"Check+%26+fix+styling"+branch%3Amain) |
| 4 | +[](https://github.com/codinglabsau/laravel-feature-flags/actions/workflows/run-tests.yml) |
9 | 5 | [](https://packagist.org/packages/codinglabsau/laravel-feature-flags) |
10 | 6 |
|
11 | | -This is where your description should go. Limit it to a paragraph or two. Consider adding a small example. |
12 | | - |
13 | | -## Support us |
14 | | - |
15 | | -[<img src="https://github-ads.s3.eu-central-1.amazonaws.com/laravel-feature-flags.jpg?t=1" width="419px" />](https://spatie.be/github-ad-click/laravel-feature-flags) |
16 | | - |
17 | | -We invest a lot of resources into creating [best in class open source packages](https://spatie.be/open-source). You can support us by [buying one of our paid products](https://spatie.be/open-source/support-us). |
18 | | - |
19 | | -We highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using. You'll find our address on [our contact page](https://spatie.be/about-us). We publish all received postcards on [our virtual postcard wall](https://spatie.be/open-source/postcards). |
20 | | - |
| 7 | +This package offers the ability to implement feature flags in your application which can be easily toggled on or off. You can also set a feature to a dynamic state where you can define custom rules around whether that feature is enabled or not. |
| 8 | +___ |
21 | 9 | ## Installation |
22 | 10 |
|
23 | | -You can install the package via composer: |
| 11 | +### Install With Composer: |
24 | 12 |
|
25 | 13 | ```bash |
26 | 14 | composer require codinglabsau/laravel-feature-flags |
27 | 15 | ``` |
28 | 16 |
|
29 | | -You can publish and run the migrations with: |
| 17 | +### Database Migrations |
30 | 18 |
|
31 | 19 | ```bash |
32 | | -php artisan vendor:publish --tag="laravel-feature-flags-migrations" |
| 20 | +php artisan vendor:publish --tag="feature-flags-migrations" |
33 | 21 | php artisan migrate |
34 | 22 | ``` |
35 | 23 |
|
36 | | -You can publish the config file with: |
| 24 | +### Publish Configuration: |
37 | 25 |
|
38 | 26 | ```bash |
39 | | -php artisan vendor:publish --tag="laravel-feature-flags-config" |
| 27 | +php artisan vendor:publish --tag="feature-flags-config" |
40 | 28 | ``` |
41 | 29 |
|
42 | | -This is the contents of the published config file: |
43 | 30 |
|
| 31 | +### Cache Store |
| 32 | +update your `.env`: |
44 | 33 | ```php |
45 | | -return [ |
46 | | -]; |
| 34 | +FEATURES_CACHE_STORE=file |
47 | 35 | ``` |
| 36 | +Note that under the hood this package uses the `rememberForever()` method for caching and that if you are using the `Memcached` driver, items that are stored "forever" may be removed when the cache reaches its size limit. |
48 | 37 |
|
49 | | -Optionally, you can publish the views using |
| 38 | +### Use Your Own Model |
50 | 39 |
|
51 | | -```bash |
52 | | -php artisan vendor:publish --tag="laravel-feature-flags-views" |
| 40 | +To use your own model, update the config and replace the existing reference with your own model: |
| 41 | + |
| 42 | +```php |
| 43 | +// app/config/feature-flags.php |
| 44 | + |
| 45 | +'feature_model' => \App\Models\Feature::class, |
| 46 | +``` |
| 47 | + |
| 48 | +Make sure to also cast the state column to a feature state enum using the `FeatureStateCast`: |
| 49 | + |
| 50 | +```php |
| 51 | +// app/Models/Feature.php |
| 52 | + |
| 53 | +use Codinglabs\FeatureFlags\Casts\FeatureStateCast; |
| 54 | + |
| 55 | +protected $casts = [ |
| 56 | + 'state' => FeatureStateCast::class |
| 57 | +]; |
53 | 58 | ``` |
54 | 59 |
|
55 | 60 | ## Usage |
56 | 61 |
|
| 62 | +Create a new feature in the database and give it a default state: |
57 | 63 | ```php |
58 | | -$features = new Codinglabs\FeatureFlags(); |
59 | | -echo $features->echoPhrase('Hello, Codinglabs!'); |
| 64 | +Feature::create([ |
| 65 | + 'name' => 'search-v2', |
| 66 | + 'state' => Codinglabs\FeatureFlags\Enums\FeatureState::on() |
| 67 | +]); |
60 | 68 | ``` |
61 | 69 |
|
62 | | -## Testing |
| 70 | +Its recommended that you seed the features to your database before a new deployment or as soon as possible after a deployment. |
63 | 71 |
|
64 | | -```bash |
65 | | -composer test |
| 72 | +A feature can be in one of three states: |
| 73 | +```php |
| 74 | +use Codinglabs\FeatureFlags\Enums\FeatureState; |
| 75 | + |
| 76 | +FeatureState::on() |
| 77 | +FeatureState::off() |
| 78 | +FeatureState::dynamic() |
| 79 | +``` |
| 80 | +### Check If A Feature Is Enabled |
| 81 | + |
| 82 | +#### Blade View |
| 83 | +```php |
| 84 | +@feature('search-v2') |
| 85 | + // new search goes here |
| 86 | +@else |
| 87 | + // legacy search here |
| 88 | +@endfeature |
| 89 | +``` |
| 90 | + |
| 91 | +#### In Your Code |
| 92 | +```php |
| 93 | +use Codinglabs\FeatureFlags\Facades\FeatureFlag; |
| 94 | + |
| 95 | +if (FeatureFlag::isEnabled('search-v2')) { |
| 96 | + // new feature code |
| 97 | +} else { |
| 98 | + // old code |
| 99 | +} |
66 | 100 | ``` |
67 | 101 |
|
68 | | -## Changelog |
| 102 | +#### Sharing features with UI (Inertiajs example) |
| 103 | +```php |
| 104 | +// config/app.php |
| 105 | + |
| 106 | +'features' => [ |
| 107 | + [ |
| 108 | + 'name' => 'search-v2', |
| 109 | + 'state' => \Codinglabs\FeatureFlags\Enums\FeatureState::dynamic() |
| 110 | + ] |
| 111 | +], |
| 112 | +``` |
69 | 113 |
|
70 | | -Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently. |
| 114 | +```php |
| 115 | +// app/Middleware/HandleInertiaRequest.php |
| 116 | + |
| 117 | +Inertia::share([ |
| 118 | + 'features' => function () { |
| 119 | + return collect(config('app.features')) |
| 120 | + ->filter(fn ($feature) => FeatureFlag::isEnabled($feature['name'])) |
| 121 | + ->pluck('name'); |
| 122 | + } |
| 123 | +]); |
| 124 | +``` |
71 | 125 |
|
72 | | -## Contributing |
| 126 | +```javascript |
| 127 | +// app.js |
| 128 | + |
| 129 | +Vue.mixin({ |
| 130 | + methods: { |
| 131 | + hasFeature: function(feature) { |
| 132 | + return this.$page.features.includes(feature) |
| 133 | + } |
| 134 | + } |
| 135 | +}) |
| 136 | +``` |
| 137 | +```html |
| 138 | +<!-- SomeComponent.vue --> |
73 | 139 |
|
74 | | -Please see [CONTRIBUTING](https://github.com/spatie/.github/blob/main/CONTRIBUTING.md) for details. |
| 140 | +<div v-if="hasFeature('search-v2')">Some cool new feature</div> |
| 141 | +``` |
| 142 | + |
| 143 | +### Updating A Features State |
| 144 | + |
| 145 | +To change a features state you can call the following methods: |
| 146 | +```php |
| 147 | +use Codinglabs\FeatureFlags\Facades\FeatureFlag; |
| 148 | + |
| 149 | +FeatureFlag::turnOn('search-v2'); |
| 150 | +FeatureFlag::turnOff('search-v2'); |
| 151 | +FeatureFlag::makeDynamic('search-v2'); |
| 152 | +``` |
| 153 | +Alternatively you can set the state directly by passing a feature state enum: |
| 154 | +```php |
| 155 | +FeatureFlag::updateFeatureState('search-v2', FeatureState::on()) |
| 156 | +``` |
| 157 | +It is recommended that you only update a features state using the above methods as it will take care of updating the cache and dispatching the feature updated event: |
| 158 | + |
| 159 | +```php |
| 160 | +\Codinglabs\FeatureFlags\Events\FeatureUpdatedEvent::class |
| 161 | +``` |
| 162 | +An example use case of the feature updated event would be if you were caching the result of a dynamic handler and need to clear that cache when a feature is updated. |
| 163 | + |
| 164 | +___ |
| 165 | +## Advanced Usage |
| 166 | + |
| 167 | +### Dynamic Features |
| 168 | + |
| 169 | +A dynamic handler can be defined in the `boot()` method of your `AppServiceProvider`: |
| 170 | +```php |
| 171 | +use Codinglabs\FeatureFlags\Facades\FeatureFlag; |
| 172 | + |
| 173 | +FeatureFlag::registerDynamicHandler('search-v2', function ($feature, $request) { |
| 174 | + return $request->user() && $request->user()->hasRole('Tester') |
| 175 | +}); |
| 176 | +``` |
| 177 | +Dynamic handlers will only be called when a feature is in the `dynamic` state. This will allow you to define custom rules around whether that feature is enabled like in the example above where the user can only access the feature if they have a tester role. |
| 178 | + |
| 179 | +Each handler is provided with the features name and current request as arguments and must return a bool value. |
| 180 | + |
| 181 | +### Default Handler For Dynamic Features |
| 182 | + |
| 183 | +You may also define a default handler which will be the catch-all handler for features that don't have an explicit handler defined for them: |
| 184 | + |
| 185 | +```php |
| 186 | +FeatureFlag::registerDefaultDynamicHandler(function ($feature, $request) { |
| 187 | + return $request->user() && $request->user()->hasRole('Tester'); |
| 188 | +}); |
| 189 | +``` |
| 190 | + |
| 191 | +An explicit handler defined using `registerDynamicHandler()` will take precedence over the default handler. If neither a default nor explicit handler has been defined then the feature will resolve to `off` by default. |
| 192 | + |
| 193 | +### Handle Missing Features |
| 194 | + |
| 195 | +Features must exist in the database otherwise a `MissingFeatureException` will be thrown. This behaviour can be turned off by explicitly handling cases where a feature doesn't exist: |
| 196 | + |
| 197 | +```php |
| 198 | +FeatureFlag::handleMissingFeaturesWith(function ($feature) { |
| 199 | + // log or report this somewhere... |
| 200 | +}) |
| 201 | +``` |
| 202 | + |
| 203 | +If a handler for missing features has been defined then an exception will **not** be thrown and the feature will resolve to `off`. |
| 204 | + |
| 205 | +## Testing |
| 206 | + |
| 207 | +```bash |
| 208 | +composer test |
| 209 | +``` |
75 | 210 |
|
76 | 211 | ## Security Vulnerabilities |
77 | 212 |
|
78 | 213 | Please review [our security policy](../../security/policy) on how to report security vulnerabilities. |
79 | 214 |
|
80 | 215 | ## Credits |
81 | 216 |
|
82 | | -- [Steve Thomas](https://github.com/codinglabsau) |
| 217 | +- [Jonathan Louw](https://github.com/JonathanLouw) |
83 | 218 | - [All Contributors](../../contributors) |
84 | 219 |
|
85 | 220 | ## License |
|
0 commit comments